编译器不同会影响浮点数计算结果

7

当我在Windows和Solaris上编译执行完全相同的浮点运算代码(使用双精度)时,我得到了略微不同的结果。

我知道由于舍入误差,结果并不完全准确。然而,我原本期望舍入误差是与平台无关的,因此在两个平台上会得到相同(稍有偏差)的结果,但事实并非如此。

这是正常现象吗?还是我的代码存在其他问题?


2
如果您发布代码的测试用例,将会很有帮助。对于这样的问题,测试用例不应超过3-4行。 - Sparr
3个回答

7

在x86上,通常大多数计算都使用80位的数量进行,除非被强制转换为双精度。我所知道的大多数其他架构都会使用双精度进行所有计算(除非另有规定)。

我不知道您是否在SPARC或x86上运行Solaris,但如果是前者,则我高度怀疑这是差异的原因。


1
如果这是Solaris x86,那么这个80位的表示仍然可能是差异的原因。在x86上的浮点运算只有在使用(内置的)x87数学协处理器时才会发生80位量;如果使用较新的SSE向量单元,则它们是普通的64位双精度。GCC默认倾向于使用x87以与旧处理器兼容;任何其他编译器都将使用SSE单元,因为即使对于非向量的东西,它也更快。 - Brooks Moses
@Brooks:谢谢!非常好的解释。 - C. K. Young
3
不用谢!顺便说一下,http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323 这个链接包含了一个相当详尽的讨论。(在Google搜索“gcc浮点数bug”时,它也是第一个结果,这说明人们经常遇到此问题!) - Brooks Moses
可以修复,将FPU控制字的PC字段设置为双精度(53位)。参考英特尔手册第8.1.5.2章节。检查您的CRT实现是否类似于_controlfp或_control87。 - Hans Passant

2
你的问题涉及到编译器,但你在不同的硬件上运行(假设你的Solaris不是x86)可能更有可能导致差异 - 硬件本身的差异。
不同的硬件平台可能使用完全不同的硬件设备(FPUs、CPUs)来执行浮点计算,从而得出不同的结果。
此外,通常可以通过一些持久设置(如无限模型、舍入模式等)配置FPU单元。不同的硬件可能具有不同的默认设置。编译器通常会生成代码,在程序启动时初始化FPU,因此初始设置也可能不同。
最后,不同的C++语言实现可能以不同的方式实现浮点语义,因此即使是相同硬件的不同C++编译器也可能得到不同的结果。

2
我相信在Windows/x86下,您的代码将以x87精度设置为53位(双精度),尽管我不确定何时设置。 在Solaris/x86上,x87 FPU可能使用其默认精度64位(扩展精度),因此会出现差异。
有一个简单的检查可以检测使用的精度(53位或64位):尝试计算类似于1e16 + 2.9999的内容,同时小心避免编译器常量折叠优化(例如定义单独的add函数来执行加法,并关闭任何可能内联函数的优化)。当使用53位精度(SSE2或双精度模式的x87)时,这给出1e16 + 2;当使用64位精度(x87扩展精度模式)时,这会给出1e16 + 4。后者的结果来自称为“双重舍入”的效果,其中加法的结果首先舍入到64位,然后再舍入到53位。 (在Python中直接进行此计算时,在32位Linux上我得到1e16 + 4,而在Windows上我得到1e16 + 2,原因完全相同。)
这是一篇非常好的文章(远远超出了经常引用的Goldberg的“What every computer scientist should know…”),该文章解释了使用x87 FPU时出现的一些问题: http://hal.archives-ouvertes.fr/docs/00/28/14/29/PDF/floating-point-article.pdf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接