我的理解是 IEEE-754 要求:(NaN cmp x)
回来false
for all cmp
运营商{==, <, <=, >, >=}
, 除了{!=}
返回true
。一个实现max()
函数可以根据任何不等式运算符来定义。
那么,问题是,如何_mm_max_ps
实施的?和{<, <=, >, >=}
,或者有点比较?
有趣的是,当禁用优化您的链接,相应的maxss
gcc 和 clang 都使用指令。两者产量:
2.000000 0.000000 0.000000 0.000000
nan 0.000000 0.000000 0.000000
这表明,鉴于:max(NaN, 2.0f) -> 2.0f
, that: max(a, b) = (a op b) ? a : b
, where op
是其中之一:{<, <=, >, >=}
。根据 IEEE-754 规则,此比较的结果始终为 false,因此:
(NaN op val)
is always错误,返回(val)
,
(val op NaN)
is always错误,返回(NaN)
启用优化后,编译器可以自由地进行预计算(c)
and (d)
在编译时。看来 clang 将结果评估为maxss
指令将 - 纠正“假设”行为。 GCC 要么依靠另一种实现max()
- 它使用 GMP 和 MPFR 库进行编译时数字 - 或者只是不小心_mm_max_ss
语义。
GCC 在 godbolt 上的 10.2 和 trunk 版本上仍然出现错误。所以我认为你发现了一个错误!我还没有回答第二部分,因为我想不出可以有效解决这个问题的万能黑客。
来自 Intel 的 ISA 参考:
如果被比较的值都是 0.0s(任一符号),则该值
返回第二个源操作数。如果第二个值
源操作数是一个 SNaN,该 SNaN 会原封不动地返回到
目的地(即不返回 SNaN 的 QNaN 版本)。
如果该指令只有一个值为 NaN(SNaN 或 QNaN),则
第二个源操作数,可以是 NaN 或有效的浮点值,
被写入结果。如果不是这种行为,而是需要
返回来自任一源操作数的 NaN,操作
可以使用一系列指令来模拟 MAXSS,例如
比较后跟 AND、ANDN 和 OR。