我做了一些数值计算,并且在使用GCC时经常遇到浮点计算问题。就我目前的目的而言,我并不太关心结果的真实精确度,但我想要这个坚定的财产:
无论我的程序中的SAME代码是什么,当它在SAME输入上运行时,我希望它提供相同的输出。
我如何强迫GCC这样做?具体而言, - fast-math和不同的-O优化的行为是什么?
我听说GCC可能会尝试变聪明,有时会在寄存器中加载浮点数,有时直接从内存中读取它们,这可能会改变浮点数的精度,导致输出不同。我怎么能避免这个?
同样,我想:
谁能告诉我这个问题的解决方法是什么?
答案 0 :(得分:1)
如果您的目标包括x86处理器,那么使用使gcc使用SSE2指令的交换机(而不是基于堆栈的历史指令)将使这些运行更像其他指令。
如果你的目标包括PowerPC处理器,使用使gcc 不的开关使用fmadd
指令(替换乘法,然后在源代码中添加)将使这些运行更像其他人。
不要使用--fast-math
:这允许编译器采取一些快捷方式,这将导致架构之间的差异。如果没有这个选项,Gcc更符合标准,因此是可预测的。
在您的应用程序中包含您自己的数学函数(exp
,sin
,...)而不是依赖于系统库中的函数只能帮助预测。
最后,即使编译器严格遵守标准(我的意思是C99),也可能存在一些差异,因为C99允许以比表达式类型所需的精度更高的精度计算中间结果。如果确实希望程序始终提供相同的结果,请写下three-address code。或者,仅使用可用于所有计算的最大精度,如果可以避免使用历史x86指令,则可以double
。在任何情况下,不使用低精度浮点数来尝试提高可预测性:根据标准中的上述条款,效果将相反。
答案 1 :(得分:-1)
我认为海湾合作委员会已经有了很好的记录,因此我不打算通过试图回答你关于其选项及其影响的部分问题来揭示自己的无知。但是,我会做出一般声明,即在考虑数字精度和性能时,阅读手册会带来很大的好处。在GCC工作的聪明人在他们的文档中付出了很多努力,阅读它是有益的(好吧,它可能是一点点乏味,但是,它是一个编译器手册而不是一个紧身胸衣开膛手)。
如果您获得相同的最后一位数字结果对您来说很重要,那么您将不仅仅关注GCC以及如何控制其行为。你需要锁定它调用的库,它运行的硬件以及我还没有想到的许多其他因素。在最糟糕的(?)情况下,您甚至可能想要,并且我已经看到了这一点,编写自己的f-p数学实现来保证跨平台的位身份。这很困难,因此很昂贵,并且使您可能不太确定自己的代码的正确性,而不是GCC使用的代码。
但是,你写了
我并不关心这个特定代码的精度,所以如果能带来可靠性,我可以很好地降低精度
向您提示问题 - 为什么不简单地使用5位小数精度作为(降低)精度的标准?这是数字计算中我们很多人一直在做的事情;我们忽略了数值分析的更精细方面,因为它们很难,并且计算时间成本高,可以规避。我正在考虑区间运算和高精度数学等问题。 (当然,如果5不适合您,请选择另一位数字。)
但好消息是,这是完全合理的:我们处理的科学数据本质上带有错误(当然我们通常不知道错误是什么,但这是另一回事)所以可以忽略十进制表示中的最后几位数,例如64位fp数。继续前进,忽略其中的一些。更好的是,你的f-p数字有多少位并不重要,你总会在计算机上进行数值计算时失去一些精确度;添加更多位只会将错误推回到最低有效位和长时间运行的计算结束。
你必须注意的情况是你有一个如此差的算法,或者算法实现不好,它会很快失去很多精度。这通常表现出任何合理大小的f-p数。如果对你来说真的有问题,你的测试套件应该暴露出来。
总结一下:你必须以某种方式处理精度的损失,并且在地毯下刷一些细节并不一定是错的。