однако значение в ymm0 равно 5.
битовый шаблон в ymm0 равен 1084227584
. Поплавок интерпретация этого числа равна 5.0
.
Но вы можете print /x $xmm0.v4_int32
увидеть шестнадцатеричное представление битов в xmm0.
Что такого особенного в регистрах XMM? кроме инструкций SIMD, они являются единственными типами регистров для хранения с плавающей запятой?
Нет, в asm все просто байты.
Некоторые компиляторы используют целочисленный регистр для копирования числа с плавающей запятой или двойного числа из одной ячейки памяти в другую, если на ней не выполняется никаких вычислений. (Целочисленные инструкции часто меньше.) Например, clang сделает это: https://godbolt.org/z/76EWMY
void copy(float *d, float *s) { *d = *s; }
# clang8.0 -O3 targeting x86-64 System V
copy: # @copy
mov eax, dword ptr [rsi]
mov dword ptr [rdi], eax
ret
Регистры XMM / YMM / ZMM являются специальными, поскольку они являются единственными регистрами, для которых инструкции FP ALU существуют для (игнорируя x87, который используется только для 80-битных long double
в x86-64) .
addsd xmm0, xmm1
(добавить скалярное двойное число) не имеет эквивалента для целочисленных регистров.
Обычно данные FP и целочисленные данные не очень смешиваются, поэтому предоставление целого отдельного набора архитектурных регистров дает больше места для большего количества данных в регистрах. (Учитывая те же ограничения кодирования команд, это выбор между 16 FP + 16 GP integer против 16 унифицированных регистров, а не против 32 унифицированных регистров).
Кроме того, основное микроархитектурное преимущество отдельного файла регистров состоит в том, что он может быть физически близок к ALU FP, а файл целочисленных регистров может быть физически близко к ALU целого числа. Подробнее см. . Существует ли какая-либо архитектура, использующая одно и то же пространство регистров для скалярных целочисленных операций и операций с плавающей запятой?
Значения float
и double
всегда сохраняются как числа с плавающей запятой? мы никогда не сможем хранить их как фиксированные точки в C или сборке?
x86-компиляторы используют float
= двоичный код IEEE75432 https://en.wikipedia.org/wiki/Single-precision_floating-point_format. (и double
= двоичный код IEEE754). Это указано как часть ABI.
Внутренне правило «как будто» позволяет компилятору делать все, что он хочет, при условии, что конечный результат идентичен. (Или с -ffast-math
, чтобы сделать вид, что математика FP ассоциативна, и предположить, что NaN / Inf невозможны.)
Компиляторы не могут просто случайно выбрать другое представление объекта для некоторого float
, на которое могут смотреть другие отдельно скомпилированные функции.
Могут быть редкие случаи для локальных пользователей, которые никогда не будут видны другим функциям, когда "человеческий компилятор" (asm для написания от руки для реализации C) может доказать, что фиксированная точка безопасна. Или, более вероятно, что значения float
были точными целыми числами, достаточно маленькими, чтобы double
не округлял их, поэтому ваша фиксированная точка могла вырождаться в целое число (за исключением, может быть, последнего шага).
Но было бы редко узнать столько о возможных значениях, если бы не было возможности делать постоянное распространение и оптимизировать все подряд. Вот почему я говорю, что человек должен быть вовлечен, чтобы доказать вещи, которые компилятор не узнает.
Я думаю, что теоретически у вас может быть реализация C, в которой использует фиксированную точку float
или double
. ISO C накладывает очень небольшие ограничения на то, что float
и double
на самом деле.
Но limits.h
константы типа FLT_RADIX
и DBL_MAX_EXP
имеют взаимодействия, которые могут не иметь смысла для формата с фиксированной запятой, который имеет постоянное расстояние между каждым представимым значением, вместо того, чтобы быть намного ближе вместе около 0 и намного дальше друг от друга для большого числа. (Погрешность округления 0,5 мяча относительно абсолютной величины вместо абсолютной.)
Тем не менее, большинство программ на самом деле не делают вещи, которые сломались бы, если бы пределы "мантиссы" и экспоненты не соответствовали тому, что вы ожидаете для DBL_MIN
и DBL_MAX
.
Еще одна интересная возможность - сделать float
и double
на основе формата Posit (аналогично традиционной плавающей запятой, но с кодировкой экспоненты переменной длины. https://www.johndcook.com/blog/2018/04/11/anatomy-of-a-posit-number/ https://posithub.org/index).
Современное оборудование, особенно процессоры Intel, имеет очень хорошую поддержку IEEE float / double, поэтому фиксированная точка часто не является победой. Есть несколько хороших SIMD-инструкций для 16-битной фиксированнойоднако, как и умножение только на верхнюю половину, и даже pmulhrsw
, который выполняет округление с фиксированной запятой.
Но общее 32-разрядное целочисленное умножение имеет худшую пропускную способность, чем упакованноеfloat
умножение. (Поскольку для SIMD ALU, оптимизированных для числа с плавающей запятой / двойного числа, требуется только 24x24-битное значение и множители на 32 бита векторного элемента. Современные процессоры Intel выполняют целочисленное умножение и сдвиг на исполнительных блоках FMA с 2 мопами на тактовую пропускную способность.)