GCC的ffast-math是否具有跨平台或编译器版本的一致性保证?

问题描述 投票:0回答:1

我想编写跨平台的C / C ++,它在不同的环境中具有可重现的行为。

我知道gcc的ffast-math可以实现各种浮点近似。这很好,但我需要两个单独编译的二进制文件来产生相同的结果。

假设我总是使用gcc,但不同的是用于Windows,Linux或其他任何版本,以及不同的编译器版本。

是否可以保证这些编译将为相同的源代码产生相同的浮点近似值?

gcc ieee-754 fast-math
1个回答
3
投票

不,并不是说它们允许特定的近似值,而是-ffast-math允许编译器假设FP数学是不相关的。即,在转换代码时忽略舍入误差以允许更高效的asm。

选择操作顺序的任何微小差异都会通过引入不同的舍入来影响结果。

较旧的编译器版本可能会选择将sqrt(x)实现为x * approx_rsqrt(x),并为-ffast-math使用Newton-Raphson迭代,因为较旧的CPU具有较慢的sqrtps指令,因此通常用相似的sqal + 3或4的近似值替换它更为合适。乘以并添加指令。对于最近的CPU,大多数代码通常不是这种情况,因此即使您使用相同的调整选项(例如默认的-mtune=generic而不是-mtune=haswell),选项所做的选择也可以在GCC版本之间进行更改。


没有-ffast-math就很难获得确定性的FP;不同操作系统上的不同库具有不同的函数实现,如sinlog(与基本操作+ - * / sqrt不同,不需要返回“正确舍入”的结果,即最大错误0.5ulp)。

如果使用x87 FP数学编译32位x86,临时(FLT_EVAL_METHOD)的额外精度可能会改变结果。 (-mfpmath=387-m32的默认值)。如果你想在这里有任何希望,你会想要避免使用32位x86。或者,如果你坚持下去,也许你可以逃脱-msse2 -mfpmath=sse ......

你提到过Windows,所以我假设你只是谈论x86 GNU / Linux,尽管Linux运行在许多其他的ISA上。

但即使只是在x86内,使用-march=haswell进行编译也能够使用FMA指令,而GCC默认使用#pragma STDC FP_CONTRACT ON(甚至跨越C语句,超出了通常的ISO C规则允许的范围。)所以实际上即使没有-ffast-math,FMA可用性也可以去除x*y的舍入暂时在x*y + z


With -ffast-math:

一个版本的gcc可能决定将循环展开2(并使用2个单独的累加器),当对数组求和时,而具有相同选项的旧版gcc可能仍然按顺序求和。

(实际上当前的gcc很糟糕,当它展开时(默认情况下)它通常仍然使用相同的(向量)累加器,因此它不会像clang那样隐藏FP延迟。例如,https://godbolt.org/z/X6DTxK对同一个变量使用不同的寄存器但它仍然只是一个累加器,在求和循环之后没有垂直加法。但希望未来的gcc版本会更好。而且gcc版本之间的区别在于它们如何处理YMM或XMM寄存器的水平和可能会在自动时引入差异矢量化)

© www.soinside.com 2019 - 2024. All rights reserved.