非正规又名次正规是 IEEE 二进制格式中指数字段 = 0 的值。 https://en.wikipedia.org/wiki/Double-precision_floating-point_format
当 FP 数学指令(不是移动或纯按位布尔值)读取这样的数字作为输入操作数时,它必须在将尾数与另一个操作数对齐时处理这种特殊情况,以及应用尾数的隐式最高位时指数暗示为 0 或非零。
是的,大多数时候输出上的 FTZ 就足够了,因为大多数浮点值是其他 FP 计算的结果。是的,FTZ 是必要的,因为正常数字上的 mul/div/add/sub 会产生不正常的结果。(添加输入需要相反的符号)。另一个 IEEE “基本” 精确舍入运算 sqrt 不能创建次正规,因为它使数字更接近 1.0。
显而易见的事情是用来perf record
找出你在哪里获得 FP 辅助,并在那里添加一些额外的检查以打印或当你在那里发现异常时添加一些东西。(然后在该分支中设置断点,以便检查情况。)
设置 FTZ 的非规范化可能来源(并非详尽无遗),即除 FP 数学运算之外:
- 使用扩展精度整数构建 FP 位模式的字符串到浮点数,如 Glibc 的
strtod
- 如果您正在读取二进制数据,请输入文件/网络。
- 其他线程或通过没有 FTZ 运行的其他进程的共享内存。(MXCSR中的FTZ/DAZ和取整模式是per-thread架构状态。话说回来,如果在启动另一个线程后只在主线程中设置FTZ,对已经启动的线程是无效的。)
- 可能对 FP 位模式进行整数操作,例如
nextafter
. 也可能作为exp
实现内部的一部分,将整数填充到 a 的指数字段中double
。
- 编译时常量值。不过,它们不必作为文字值出现在源代码中。例如
static double foo = DBL_MIN / 4.0;
将是编译时非规范化。但是你会在.rodata
or中找到它们.data
。非常量非零静态/全局变量进入.data
.
显然,任何使用整数的 FP 位模式的手动操作也可以做到这一点。 如何在没有 AVX2 的情况下使用字节中的位在 ymm 寄存器中设置 dwords?(vmovmskps 的倒数)如果我没有花费额外的指令来避免它,可能会产生比较的非正规输入,但这是编译器不会为你做的不寻常的手动向量化技巧。
立即数
x86 没有 FP 立即数;你必须mov rax, imm64
/movq xmm0, rax
或类似的。但是编译器不会这样做,因为从.rodata
.
用于指导vmovsd 0x9498(%rip),%xmm0
vmovsd
只是一个负载,并且总是准确地复制 64 位;在架构上等效于vmovq
SIMD 整数负载。
它不通过 ALU 运行该值,因此没有 MXCSR 位对vmovsd
FP shuffle 等有任何影响。只有执行实际 FP 数学运算并可能引发 FP 异常的指令才会受到影响。您可以通过查看 asm 手册条目的异常部分来判断。例如,在根据指定模式四舍五入之前roundsd
,确实服从 DAZ 可能将输入四舍五入为零。