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

时间:2011-10-21 09:09:39

标签: c floating-point

我们正在使用C语言中的数值例程库。我们还不确定是否可以使用单精度(float)或双精度(double),因此我们定义了在我们决定之前输入SP作为别名:

typedef float SP;

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

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

-typedef double SP;
+typedef float SP;

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

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

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

澄清

我担心我可能不够清楚:我们使用Git commit 2f3f671使用双精度,并且单元测试在两台机器上同样通过。然后我们有Git commit 46f2ba,我们将其更改为单精度,这里测试仍然在64位机器上传递,但不在上传递给32-位机。

2 个答案:

答案 0 :(得分:8)

您遇到了通常被称为'x87超精确度'的错误“'。

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

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

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

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

答案 1 :(得分:-1)

IIRC,'double'的精度只有 才能> = 'float'的精度。因此,在一个实现中,'float'和'double'中的实际位数可能相同,而另一个实现不同。这可能是平台中32位/ 64位差异的原因,但可能不是。