为什么相同的代码会在32位与64位计算机上产生不同的数字结果?

lin*_*lof 6 c floating-point

我们正在使用C语言中的数字例程库.我们还不确定是否可以使用单精度(float)或double(double),因此我们将类型定义SP为别名,直到我们决定:

typedef float SP;
Run Code Online (Sandbox Code Playgroud)

当我们运行我们的单元测试时,它们都会通过我的机器(一个64位的Ubuntu),但它们在我的同事(一个错误地安装在64位机器上的32位Ubuntu)上失败了.

使用Git的bisect命令,我们发现确切的差异开始在他的机器和我的机器之间产生不同的结果:

-typedef double SP;
+typedef float SP;
Run Code Online (Sandbox Code Playgroud)

换句话说,从双精度到单精度会在我们的机器上产生数值上不同的结果(在最坏情况下约为1e-3相对差异).

我们相当肯定,我们永远不会将无符号的整数与负数签名的整数进行比较.

为什么数字例程库会在32位操作系统和64位系统上产生不同的结果?

澄清

我担心我可能不够清楚:我们有2f3f671使用双精度的Git提交,并且单元测试在两台机器上同样通过.然后我们有git的承诺46f2ba,我们改为单精度,这里的测试仍然通过64位的计算机上,但不能在32位机器上.

Ste*_*non 8

你遇到了通常被称为'x87过剩精度'的错误"'.

简而言之:历史上,(几乎)x86处理器上的所有浮点计算都是使用x87指令集完成的,默认情况下,它运行在80位浮点类型上,但可以设置为单或双操作 - 通过控制寄存器中的某些位精确(几乎).

如果在x87控制寄存器的精度设置为双精度或扩展精度时执行单精度运算,则结果将与以单精度执行相同操作时产生的结果不同(除非编译器是非常小心并存储每个计算的结果并重新加载它以强制在正确的位置进行舍入.)

您在32位上运行的代码使用x87单元进行浮点计算(显然控制寄存器设置为双精度),因此遇到上述问题.您在64位上运行的代码使用SSE [2,3,...]指令进行浮点计算,这些指令提供本机单精度和双精度运算,因此不会带来过多的精度.这就是您的结果不同的原因.

通过告诉编译器即使在32位(-mfpmath=sse使用GCC)上也使用SSE进行浮点计算,您可以解决这个问题(到某一点).即使这样,也不能保证比特精确的结果,因为您链接的各种库可能使用x87,或者根据体系结构简单地使用不同的算法.