当前位置: 首页 > 知识库问答 >
问题:

_mm_max_ss在clang和gcc之间有不同的行为

岳泉
2023-03-14

我试图使用clang和gcc交叉编译一个项目,但在使用\u mm\u max\u 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-数学-fno-fast-数学,但似乎没有效果。有什么想法可以让编译器之间的行为相似吗?

这里是锁销连接

共有1个答案

吴经略
2023-03-14

我的理解是,IEEE-754要求:(NaN cmp x)为所有cmp运算符{==,返回false

那么,问题来了,_mm_max_ps是如何实现的?用{

有趣的是,当在链接中禁用优化时,gcc和clang都使用相应的maxss指令。两者都产生:

2.000000 0.000000 0.000000 0.000000 
nan 0.000000 0.000000 0.000000

这表明,给定:max(NaN,2.0f)-

(NaN op val)总是false,返回(val)
(val op NaN)总是false,返回(NaN)

启用优化后,编译器可以在编译时自由地预计算(c)(d)。clang对结果的评估似乎是因为maxss指令将纠正“仿佛”行为。GCC要么依赖于max()的另一个实现——它使用GMP和MPFR库进行编译时数字运算——要么就是对\u mm\u max\u ss语义不太在意。

GCC在godbolt上的10.2和trunk版本中仍然存在错误。所以我想你发现了一只虫子!我还没有回答第二部分,因为我想不出一个通用的黑客可以有效地解决这个问题。

从英特尔的ISA参考:

如果要比较的值均为0.0(任意一个符号),则返回第二个源操作数中的值。如果第二个源操作数中的值是SNaN,则该SNaN将原封不动地返回到目标(即,不返回SNaN的QNaN版本)。

如果此指令只有一个值是NaN(SNaN或QNaN),则第二个源操作数(NaN或有效浮点值)将写入结果。如果不是此行为,而是要求返回来自任一源操作数的NaN,则可以使用一系列指令来模拟MAXSS的操作,例如,比较后跟AND、ANDN和OR。

 类似资料:
  • 下面的例子可以解释我的意思: <代码>自动p=标准::使\u共享 变量是默认初始化的(因此具有垃圾值)还是值初始化的(因此具有零值)?我在GCC 5.2和clang 3.6上进行了测试,前者进行值初始化,后者进行默认初始化。我想知道标准对此有什么规定?在我看来,在这种情况下,现代C肯定应该执行值初始化。

  • 根据这篇关于c语言中未定义行为优化的有趣文章,表达式

  • 考虑到这个辅助函数: 它应该将基本类型(和字符串)转换为字符串表达式,当我像这样使用它时,MSVC会出现编译错误: 有了clang,编译起来没有任何问题,但有了MSVC,我明白了: 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 2. 显然,编译器会考虑

  • GCC接受以下代码: 但是clang拒绝它并带有以下错误: 这似乎表明GCC和clang在重载解析期间执行某些操作的顺序有所不同。GCC似乎在尝试实例化模板候选者的返回类型之前抛弃了模板候选者,因为第二个参数(vs.)中的类型不匹配,而clang似乎反过来做。 谁是对的? 我相信这个问题对模板库的作者有着重要的意义。具体来说,如果clang是正确的,那么模板的作者将不得不做额外的工作,将错误转化为

  • 当一个类具有 constexpr 成员函数并且该成员函数正在 constexpr 上下文中的 l 值对象上求值时,clang 和 gcc 不同意结果是否为 constexpr 值。为什么?是否有既不需要默认可构造性也不需要复制可构造性的解决方法? 当对象按值传递时,两个编译器都会成功编译。 Clang版本trunk,8,7: 和 gcc 版本主干,8.1、7.4:编译没有错误 https://go

  • 我知道movzx可以用于打破依赖关系,但我偶然发现Clang和GCC都使用了movzx,我真的看不出它们有什么好处。下面是我在godbolt上尝试的一个简单示例: 对于gcc12-O3: 如果我理解正确,这里的第一个movzx打破了对前一个eax值的依赖,但是第二个movzx在做什么?我不认为它可以打破任何依赖,也不应该影响结果。 有了clang14-O3,就更奇怪了: 它在movzx似乎更合理的