Денорма, иначе субнормальный, - это значение с полем экспоненты = 0 в двоичном формате IEEE. https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Когда математическая инструкция FP (не перемещаемая или чисто побитовая логическая) считывает такое число как входной операнд, она должна обрабатывать этот особый случай при выравнивании мантиссы с другой операнд, и при применении неявного верхнего бита мантиссы, который подразумевается показателем степени, равным 0 или отличным от нуля.
Да, большую часть времени FTZ на выходе является достаточным, поскольку большинство значений с плавающей запятой являются результаты других вычислений ФП. И да, FTZ необходим, потому что mul / div / add / sub для нормальных чисел может создать ненормальный результат . (Для добавления входов нужны противоположные знаки). Другая точно округленная операция IEEE "basi c", sqrt, не может создавать субнормалей, потому что она приближает числа к 1.0.
Очевидной вещью будет использование perf record
, чтобы выяснить, где вы Вы получаете FP-помощь и добавляете некоторые дополнительные чеки, чтобы напечатать или что-то еще, когда вы найдете там ненормальный. (Затем установите точку останова в этой ветви, чтобы вы могли изучить ситуацию.)
Возможные источники ненормальных значений (не исчерпывающих) с установленным FTZ, т. Е. Отличных от математических операций FP:
- String-to-float, который формирует битовый шаблон FP с целым числом расширенной точности, как Glib c '
strtod
- Входные файлы / сеть, если вы' повторное чтение двоичных данных.
- Другие потоки или через разделяемую память из других процессов, работающих без FTZ. (FTZ / DAZ и режим округления в MXCSR являются архитектурным состоянием для каждого потока. Кстати, если вы установите FTZ только в главном потоке после запуска другого потока, он не будет действовать для уже начатый поток.)
- Возможно целочисленное манипулирование битовыми шаблонами FP, например
nextafter
. Также возможно как часть внутренних компонентов реализации exp
, которая вставляет целое число в поле экспоненты double
. - Постоянные значения времени компиляции. Они не должны появляться в исходном коде как буквальное значение. например,
static double foo = DBL_MIN / 4.0;
будет денормальным во время компиляции. Но вы найдете их в .rodata
или .data
. Неконстантные ненулевые состояния c / глобальные переменные go в .data
.
Очевидно, что любое ручное манипулирование битовыми шаблонами FP с использованием целочисленных данных тоже может это сделать. Как использовать биты в байте для установки слов в регистре ymm без AVX2? (Инверсия vmovmskps) мог бы привести к ненормальным входам для сравнения, если бы я не потратил лишнюю инструкцию, чтобы ее избежать, но это необычный трюк ручной векторизации, который компиляторы не сделали бы для вас.
непосредственные операнды
x86 не имеет немедленных FP; вам нужно будет mov rax, imm64
/ movq xmm0, rax
или подобное. Но компиляторы этого не делают, потому что обычно более эффективно загружать из .rodata
.
для инструкции vmovsd 0x9498(%rip),%xmm0
vmovsd
- это просто загрузка и всегда точно копирует 64 бита; архитектурно эквивалентен vmovq
SIMD-целочисленной загрузке.
Он не пропускает значение через ALU, поэтому никакие биты MXCSR не влияют на vmovsd
, FP shuffles и т. д. c. Это влияет только на инструкции, которые выполняют фактическую математику FP и могут вызывать исключения FP Вы можете сказать, посмотрев на раздел исключений в записи руководства asm. Например, roundsd
подчиняется DAZ для возможного округления ввода до нуля до округления в соответствии с указанным режимом.