AVX SIMD代码中NaN符号差异:编译器常量传播问题问询
检测SIMD寄存器中±无穷大/NaN的编译器行为差异
我正在研究如何检测AVX SIMD寄存器中哪些通道的浮点数是±无穷大或±NaN,运行时发现异常行为后,分析了不同编译器在不同优化级别下的输出差异。
对应的C++代码如下:
#include <immintrin.h> #include <cstdio> #include <limits> #include <cstdint> static constexpr float inf = std::numeric_limits<float>::infinity(); static constexpr float qnan = std::numeric_limits<float>::quiet_NaN(); static constexpr float snan = std::numeric_limits<float>::signaling_NaN(); int main() { __m256 a = _mm256_setr_ps(0.0f, 1.0f, inf, -inf, qnan, -qnan, snan, -snan); __m256 mask = _mm256_sub_ps(a, a); // Extract masks as integers int mask_bits = _mm256_movemask_ps(mask); std::printf("Mask for INFINITY or NaN: 0x%x\n", mask_bits); #define PRINT_ALL #ifdef PRINT_ALL float data_field[8]; float mask_field[8]; _mm256_storeu_ps(data_field, a); _mm256_storeu_ps(mask_field, mask); for (int i = 0; i < 8; ++i) { std::printf("isfinite(%f) = %x = %f\n", data_field[i], ((int32_t*)(char*)mask_field)[i], mask_field[i]); } #endif return 0; }
不同编译器的输出结果存在差异,且结果会随优化级别变化——部分编译器会在编译时通过逻辑直接执行代码,将结果硬编码到打印语句中,而非在运行时实际计算。此外,启用PRINT_ALL宏手动打印所有结果也会影响输出。
掩码输出差异详情
未启用PRINT_ALL时
- GCC 13.1
-O0:0xac- 新生成的NaN为-nan;保留输入NaN的符号 - GCC 13.1
-O1:0x5c- 新生成的NaN为-nan;翻转输入NaN的符号 - Clang 16.0.0
-O0:0xac - Clang 16.0.0
-O1:0xa0- 新生成的NaN为+nan;保留输入NaN的符号 - ICX 2022.2.1
-O0:0xac - ICX 2022.2.1
-O1:0xa0
启用PRINT_ALL后
- GCC 13.1
-O0:0xac - GCC 13.1
-O1:0xac(与硬件执行结果一致) - Clang 16.0.0
-O0:0xac - Clang 16.0.0
-O1:0xa0(无变化) - ICX 2022.2.1
-O0:0xac - ICX 2022.2.1
-O1:0xa0(无变化)
关键说明
“新生成的NaN”指inf - inf或-inf - -inf的计算结果——这类计算输入均非NaN但输出为NaN,对应十六进制掩码结果低四位的高2位(即0x?C或0x?0中的?部分)。低四位的低2位来自0-0和1-1的计算结果,根据IEEE浮点标准,默认舍入模式下有限数自减应输出+0.0。
核心问题
这种现象是符合某些未知规则的预期行为,还是GCC、Clang、ICX这三款编译器均存在bug?(暂未测试MSVC)
注:
-fno-strict-aliasing选项不影响结果,因此((int32_t*)(char*)mask_field)[i]并非问题诱因。
内容的提问来源于stack exchange,提问作者Martijn Courteaux
相关产品推荐
相关产品推荐

