GNU GCC不将浮点除法舍入到最接近的值

2020-06-28 06:44:14

我知道浮点算术在现代计算机上有点疯狂。例如,浮点数不具有关联性:

但是,至少在我的经验中,这是相当一致的。您不应该简单地假设像关联性这样的花哨属性在现实世界中起作用。

如果上帝真的存在,可变比率就会是0.50178230318,故事就到此为止了。不幸的是,没有正好为0.50178230318的浮点数。相反,它落在浮点数0.501782303179999944和浮点数0.501782303180000055之间。

重要的是要更精确一点。64位浮点标准将数字表示为53位尾数,后跟2的幂。所以,让我们用计算机的方式把它说清楚:

我们必须选择尾数4519653187245114或尾数4519653187245115。使用64位浮点数无法精确表示介于两者之间的任何内容。那么0.50178230318到底落在哪里呢?我们大约有…。

让我们启动适用于x86系统的GNU GCC 7编译器,并运行以下C/C++程序:

#include<;stdio.h>;int main(){Double x=50178230318.0;Double y=100000000000.0;Double Ratio=x/y;printf(";x/y=%18.18f\n";,Ratio);}。

因此,GNU GCC实际上选择了最小和最远的值,而不是最近的值。即使您专门设置了要求编译器舍入到最近的值(fesetround(FE_TONEAREST)),它仍然是真的。

与GNU GCC团队类似的报告即使不是成百上千,也有几十份。它们被标记为无效。

让我回顾一下:GNU GCC编译器可能会将两个浮点数的除法结果舍入为一个不是最接近的值。而且它也不被认为是一个漏洞。

解释是编译器首先使用80位舍入到最近,然后再次舍入。这就是花哨的数字爱好者所说的FLT_EVAL_METHOD=2。

但是,即使添加了优化标志(如-O2),FLT_EVAL_METHOD的值也保持为2,但结果仍将更改。原因是优化器在编译时计算出解决方案,并忽略FLT_EVAL_METHOD值。

正如专家建议的那样,您还可以尝试向GNU GCC传递标志-msse-mfpath=sse,但是正如我的脚本所演示的那样,它不能解决问题(然后您会得到FLT_EVAL_METHOD=-1)。您还需要添加适当的目标(例如,-msse-mfpath=sse-march=pentium4)。

如果你不明白为什么所有这一切都不会成为一个错误,欢迎加入俱乐部。您可以在Godbolt上检查程序集。