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

我们正在研究C中的数值例程库。我们还不确定我们是使用单精度( float )还是double( double ),所以我们将类型SP定义为别名,直到我们决定:

 typedef float SP; 

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

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

 -typedef double SP; +typedef float SP; 

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

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

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

澄清

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

你遇到了通常被称为’x87过剩精度’的错误“’。

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

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

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

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

IIRC,’double’的精度只需要 > = ‘float’的精度。 因此,在一个实现中,’float’和’double’中的实际位数可能相同,而另一方面它们不同。 这可能是平台中32位/ 64位差异的原因,但可能不是。