_mm_max_ss 在 clang 和 gcc 之间有不同的行为
_mm_max_ss has different behavior between clang and gcc
我正在尝试使用 clang 和 gcc 交叉编译一个项目,但是在使用 _mm_max_ss
例如
时我看到了一些奇怪的差异
__m128 a = _mm_set_ss(std::numeric_limits<float>::quiet_NaN());
__m128 b = _mm_set_ss(2.0f);
__m128 c = _mm_max_ss(a,b);
__m128 d = _mm_max_ss(b,a);
现在我预计涉及 NaN 时会出现 std::max
类型行为,但 clang 和 gcc 会给出不同的结果:
Clang: (what I expected)
c: 2.000000 0.000000 0.000000 0.000000
d: nan 0.000000 0.000000 0.000000
Gcc: (Seems to ignore order)
c: nan 0.000000 0.000000 0.000000
d: nan 0.000000 0.000000 0.000000
_mm_max_ps 在我使用它时做了预期的事情。我试过使用-ffast-math
、-fno-fast-math
,但似乎没有效果。有什么想法可以使编译器之间的行为相似吗?
神箭linkhere
我的理解是 IEEE-754 要求:(NaN cmp x)
to return false
for all cmp
operators {==, <, <=, >, >=}
,除了 {!=}
,其中 returns true
。 max()
函数的实现可以根据任何不等式运算符来定义。
那么,问题来了,_mm_max_ps
是如何实现的呢?跟{<, <=, >, >=}
,还是有点比较?
有趣的是,当 禁用 优化 link 时,相应的 maxss
指令被两者使用gcc 和 clang。两者产量:
2.000000 0.000000 0.000000 0.000000
nan 0.000000 0.000000 0.000000
这表明,给定:max(NaN, 2.0f) -> 2.0f
,即:max(a, b) = (a op b) ? a : b
,其中 op
是以下之一:{<, <=, >, >=}
。使用 IEEE-754 规则,此比较的结果始终为假,因此:
(NaN op val)
总是 错误,returning (val)
,
(val op NaN)
总是 错误,returning (NaN)
启用优化后,编译器可以在编译时自由预计算 (c)
和 (d)
。 clang 似乎将结果评估为 maxss
指令会纠正 'as-if' 行为。 GCC 要么退回到 max()
的另一种实现——它使用 GMP 和 MPFR 库进行编译时数字——要么只是对 _mm_max_ss
粗心大意语义。
GCC 在 godbolt 上的 10.2 和主干版本仍然出错。所以我认为你发现了一个错误!我还没有回答第二部分,因为我想不出可以有效解决这个问题的万能黑客。
来自英特尔的 ISA 参考:
If the values being compared are both 0.0s (of either sign), the value
in the second source operand is returned. If a value in the second
source operand is an SNaN, that SNaN is returned unchanged to the
destination (that is, a QNaN version of the SNaN is not returned).
If only one value is a NaN (SNaN or QNaN) for this instruction, the
second source operand, either a NaN or a valid floating-point value,
is written to the result. If instead of this behavior, it is required
that the NaN from either source operand be returned, the action of
MAXSS can be emulated using a sequence of instructions, such as, a
comparison followed by AND, ANDN and OR.
我正在尝试使用 clang 和 gcc 交叉编译一个项目,但是在使用 _mm_max_ss
例如
__m128 a = _mm_set_ss(std::numeric_limits<float>::quiet_NaN());
__m128 b = _mm_set_ss(2.0f);
__m128 c = _mm_max_ss(a,b);
__m128 d = _mm_max_ss(b,a);
现在我预计涉及 NaN 时会出现 std::max
类型行为,但 clang 和 gcc 会给出不同的结果:
Clang: (what I expected)
c: 2.000000 0.000000 0.000000 0.000000
d: nan 0.000000 0.000000 0.000000
Gcc: (Seems to ignore order)
c: nan 0.000000 0.000000 0.000000
d: nan 0.000000 0.000000 0.000000
_mm_max_ps 在我使用它时做了预期的事情。我试过使用-ffast-math
、-fno-fast-math
,但似乎没有效果。有什么想法可以使编译器之间的行为相似吗?
神箭linkhere
我的理解是 IEEE-754 要求:(NaN cmp x)
to return false
for all cmp
operators {==, <, <=, >, >=}
,除了 {!=}
,其中 returns true
。 max()
函数的实现可以根据任何不等式运算符来定义。
那么,问题来了,_mm_max_ps
是如何实现的呢?跟{<, <=, >, >=}
,还是有点比较?
有趣的是,当 禁用 优化 link 时,相应的 maxss
指令被两者使用gcc 和 clang。两者产量:
2.000000 0.000000 0.000000 0.000000
nan 0.000000 0.000000 0.000000
这表明,给定:max(NaN, 2.0f) -> 2.0f
,即:max(a, b) = (a op b) ? a : b
,其中 op
是以下之一:{<, <=, >, >=}
。使用 IEEE-754 规则,此比较的结果始终为假,因此:
(NaN op val)
总是 错误,returning (val)
,
(val op NaN)
总是 错误,returning (NaN)
启用优化后,编译器可以在编译时自由预计算 (c)
和 (d)
。 clang 似乎将结果评估为 maxss
指令会纠正 'as-if' 行为。 GCC 要么退回到 max()
的另一种实现——它使用 GMP 和 MPFR 库进行编译时数字——要么只是对 _mm_max_ss
粗心大意语义。
GCC 在 godbolt 上的 10.2 和主干版本仍然出错。所以我认为你发现了一个错误!我还没有回答第二部分,因为我想不出可以有效解决这个问题的万能黑客。
来自英特尔的 ISA 参考:
If the values being compared are both 0.0s (of either sign), the value in the second source operand is returned. If a value in the second source operand is an SNaN, that SNaN is returned unchanged to the destination (that is, a QNaN version of the SNaN is not returned).
If only one value is a NaN (SNaN or QNaN) for this instruction, the second source operand, either a NaN or a valid floating-point value, is written to the result. If instead of this behavior, it is required that the NaN from either source operand be returned, the action of MAXSS can be emulated using a sequence of instructions, such as, a comparison followed by AND, ANDN and OR.