GCC 的 手册页 指出
-funsafe-math-optimizations
允许优化“(a) 假设参数和结果有效,(b) 可能违反 IEEE 或 ANSI 标准”,但这不是很精确,不是吗?
在这种情况下,“无效”参数可能是什么? NaN?无穷大?次正规?负数到
sqrt()
?
结果允许偏离 IEEE 或 ANSI 标准多远?它是“仅仅”像操作关联性和排序这样的东西,还是可能包括例如。与 NaN 的比较正确还是与无穷大的比较错误?存储的变量在使用后是否可以重新舍入(例如,对于 variables
x
和 y
,(x == y) + (x == y)
可以计算为 1)? isinf()
/isnan()
可以停止工作吗?
GCC 开发人员对于此类问题是否遵循任何特定的系统或规则,或者不同版本的答案可能会有很大差异?
根据gcc.gnu.org(我的粗体):
“内置函数[其]具有诸如__builtin_sqrt”之类的名称此模式可实现允许“任意重新关联”和“不保证准确性的转换”的优化。它也不会尝试保留零的符号。 这包括您提到的关联重新排序
,以及
,当它们“可能具有较低的精度或被限制在较小的域”时应用 也许举个例子会有帮助。根据 IEEE 的规则,以下代码是安全的:
从
a > b
开始,从a中减去b不能得到零。但在某些 ARMv7 CPU 上,使用 NEON 时
可以。结果将是除以零异常,从而导致程序崩溃。因此,GCC 在 ARMv7 CPU 上不使用 NEON。
基本上,-funsafe-math-optimizations
会影响精度。通过使用此标志,您可以告诉 GCC,如果不满足 IEEE 标准设定的准确性保证也没关系,只要有速度优势(“我用准确性换取速度”)。
当不需要尽可能高的准确度时,以准确度换取速度通常不是一个大问题,但是,如上所述,在某些情况下,稍微低一点的准确度实际上会导致代码崩溃。是的,指令的关联性和重新排序也会影响结果的准确性;所以通常编译器只会在满足所有 IEEE 保证的情况下才会这样做,但使用该选项,即使没有满足,它也会这样做。
这与 NEON 有什么关系?好吧,NEON 单元通常比 ARM 上的 VFP 单元更快,因此使用 NEON 执行 FPU 操作将为您带来速度优势,但 NEON 单元在某些 ARMv7 CPU 上存在准确性问题。它在 ARMv8 及更高版本的 CPU 上完全准确。