Clang下MMX intrinsics引发long double赋值出现NaN问题求助
问题描述
这段代码用Clang编译时,会在assert(!isnan(x))处崩溃;但添加编译选项-DWITH_MMX=0后程序就能正常运行。该现象在Compiler Explorer和本地macOS环境中均存在。
无法理解为何启用MMX intrinsics时,将42.0赋值给long double类型变量会产生NaN,请求分析其中原因。
代码如下:
// cc -O0 -DWITH_MMX=1 -o nan nan.c #include <mmintrin.h> #include <stdio.h> #include <assert.h> #include <math.h> int main() { #if WITH_MMX __m64 a = _m_from_int(4); __m64 b = _m_from_int(8); __m64 ab = _m_paddb(a, b); int c = _m_to_int(ab); assert(c == 12); #endif long double x = 42.0L; assert(!isnan(x)); // 42.0 不应为NaN printf("done\n"); }
原因分析
核心问题是MMX指令集与x87浮点寄存器的状态冲突:
- MMX指令会复用x87浮点单元的寄存器,且执行后不会自动恢复x87的状态。在MMX操作完成后,x87的控制字或寄存器状态可能处于异常,导致后续依赖x87的浮点操作出错。
- macOS平台上的
long double是80位扩展精度类型,完全依赖x87浮点单元实现。当MMX指令破坏x87状态后,后续对long double的赋值操作会出现异常,让正常的数值被错误识别为NaN。
解决方法是在MMX代码块末尾调用_mm_empty()函数,它会专门恢复x87浮点单元的状态,确保后续浮点操作正常执行。修改后的代码如下:
// cc -O0 -DWITH_MMX=1 -o nan nan.c #include <mmintrin.h> #include <stdio.h> #include <assert.h> #include <math.h> int main() { #if WITH_MMX __m64 a = _m_from_int(4); __m64 b = _m_from_int(8); __m64 ab = _m_paddb(a, b); int c = _m_to_int(ab); assert(c == 12); _mm_empty(); // 恢复x87浮点单元状态 #endif long double x = 42.0L; assert(!isnan(x)); printf("done\n"); }
内容的提问来源于stack exchange,提问作者user2962393
相关产品推荐
相关产品推荐

