_mm_comieq_ss在Clang与GCC中的行为差异及替代方案咨询
SIMD相等性检查中GCC与Clang处理NaN的差异问题
我编写了一段用于检查变量相等性的SIMD代码,但涉及NaN时,GCC与Clang的运行结果不一致。代码如下:
#include <immintrin.h> #include <iostream> #include <limits> bool equal(__m128 a, __m128 b){ return _mm_comieq_ss(a,b) == 1; } int main() { __m128 a, b, c; a = _mm_set_ss(std::numeric_limits<float>::quiet_NaN()); b = _mm_set_ss(1.0f); c = _mm_set_ss(1.0f); std::cout << "comieq(a,b):" << equal(a,b) << std::endl; std::cout << "comieq(b,a):" << equal(b,a) << std::endl; std::cout << "comieq(b,c):" << equal(b,c) << std::endl; std::cout << "comieq(a,a):" << equal(a,a) << std::endl; return 0; }
GCC和Clang的输出结果不同:
GCC输出:
comieq(a,b):1 comieq(b,a):1 comieq(b,c):1 comieq(a,a):1
Clang输出:
comieq(a,b):0 comieq(b,a):0 comieq(b,c):1 comieq(a,a):0
请问造成这种差异的原因是什么?有没有可以实现寄存器相等性检查的一致方法?
差异原因
_mm_comieq_ss对应x86架构的COMISS指令,该指令的比较行为受FPU控制字中NaN比较掩码的影响:
- Clang默认遵循IEEE 754标准的行为:任何涉及NaN的比较结果都为假,因此NaN和任何值(包括自身)比较都返回0。
- GCC默认会修改FPU控制字,将NaN比较掩码设置为“认为NaN相等”,因此只要使用
COMISS指令比较包含NaN的操作数,都会返回“相等”的结果(即返回1)。
一致的解决方法
根据需求选择以下两种方案:
1. 按二进制位完全匹配检查(区分不同编码的NaN)
如果需要判断两个SIMD寄存器的低32位单精度浮点数二进制完全相同(包括相同编码的NaN),可以使用按位相等指令:
bool equal(__m128 a, __m128 b){ // _mm_cmpeq_ps生成掩码,对应位相等则为全1,否则全0 // _mm_movemask_ps将掩码转为整数,取最低位判断低32位是否相等 return (_mm_movemask_ps(_mm_cmpeq_ps(a, b)) & 1) != 0; }
这种方式不受FPU控制字影响,在GCC和Clang下行为完全一致:相同二进制的NaN会返回true,不同编码的NaN或NaN与其他值比较返回false。
2. 遵循IEEE754标准的数值相等检查
如果需要严格遵循IEEE754标准(NaN与任何值比较都为假),可以直接使用_mm_cmpeq_ps指令(该指令的行为不受FPU控制字影响,严格遵循IEEE754):
bool equal(__m128 a, __m128 b){ // _mm_cmpeq_ps的结果中,低32位全1表示相等,否则为0 return _mm_extract_ps(_mm_cmpeq_ps(a, b), 0) == 0xFFFFFFFF; }
也可以在程序启动时设置FPU控制字,让GCC采用默认的IEEE754比较规则(仅针对COMISS指令生效):
#include <cfenv> int main(){ fesetround(FE_TONEAREST); // 恢复默认FPU控制字,遵循IEEE754比较规则 // ... 原有代码 ... }
内容的提问来源于stack exchange,提问作者Biggy Smith
相关产品推荐
相关产品推荐

