【发布时间】:2019-12-31 14:01:55
【问题描述】:
在某些情况下,您知道某个浮点表达式将始终为非负数。例如,在计算向量的长度时,会使用sqrt(a[0]*a[0] + ... + a[N-1]*a[N-1])(注意:我am知道std::hypot,这与问题无关),以及平方根下的表达式显然是非负的。但是,GCC outputs 的以下程序集为sqrt(x*x):
mulss xmm0, xmm0
pxor xmm1, xmm1
ucomiss xmm1, xmm0
ja .L10
sqrtss xmm0, xmm0
ret
.L10:
jmp sqrtf
也就是说,它将x*x的结果与零进行比较,如果结果非负,则执行sqrtss指令,否则调用sqrtf。
所以,我的问题是:如何强制 GCC 假设 x*x 始终为非负数,以便跳过比较和 sqrtf 调用,而不编写内联汇编?
我想强调的是,我对本地解决方案感兴趣,而不是像 -ffast-math、-fno-math-errno 或 -ffinite-math-only 这样的事情(尽管这些确实解决了问题,感谢 ks1322、harold 和 Eric cmets中的Postpischil)。
此外,“强制 GCC 假设 x*x 为非负数”应解释为 assert(x*x >= 0.f),因此这也排除了 x*x 为 NaN 的情况。
我可以接受特定于编译器、特定于平台、特定于 CPU 等的解决方案。
【问题讨论】:
-
x*x不一定为零或正数。它可能是一个 NaN。不过,我不确定 GCC 在这里处理的是什么。 -
-fno-math-errno是更安全的选项,它还删除了对sqrtf的调用 -
@EricPostpischil 当然!不过,我仍然想强制编译器认为它不是 NaN。
-
添加
-ffinite-math-only告诉 GCC 它可以假设没有无穷大或 NaN。使用它消除了分支和对sqrtf的调用。由于sqrtf的无穷大不是错误,这证实了 GCC 在问题中的示例代码中关注的是 NaN。不幸的是,我没有看到一个开关只是说假设没有 NaN,而不是假设没有 NaN 或无穷大,并且在sqrt之前插入if (std::isnan(x)) return x;不会导致 GCC 识别x*x不能是 NaN。 -
@dan04:开关没有说你不能有 NaN;它说编译器可能会假设没有 NaN。因此,您有责任避免 NaN 或承担后果。如果您评估两个无穷大的商,则后续代码可能已在假设未生成 NaN 的情况下进行了优化,因此它可能会走错路,例如。
标签: c++ gcc assembly floating-point micro-optimization