考虑以下代码:
double v1 = double.MaxValue;
double r = Math.Sqrt(v1 * v1);
在32位机器上,r = double.MaxValue 在64位机器上,r = Infinity。 我们在32位的机器上开发,直到客户通知我们才意识到这个问题。为什么会出现这种不一致性?如何避免这种情况发生?
考虑以下代码:
double v1 = double.MaxValue;
double r = Math.Sqrt(v1 * v1);
在32位机器上,r = double.MaxValue 在64位机器上,r = Infinity。 我们在32位的机器上开发,直到客户通知我们才意识到这个问题。为什么会出现这种不一致性?如何避免这种情况发生?
double r = Math.Sqrt(v1 * v1);
00000006 fld dword ptr ds:[009D1578h]
0000000c fsqrt
0000000e fstp qword ptr [ebp-8]
在发布模式下,代码优化器删除了fmul指令。毫无疑问,这是由于它看到double.MaxValue时触发的。这是一个错误,您可以在connect.microsoft.com上报告它。但我很确定他们不会修复它。
这篇文章与为什么在不同的计算机上进行浮点数计算会得到不同的结果?几乎一模一样。我的回答也适用于这个问题。简而言之,由于硬件细节的不同,不同的硬件允许给出更或更少准确的结果。
如何避免这种情况发生?既然问题出在芯片上,你有两个选择:(1)不要使用浮点数进行任何数学运算,而是使用整数进行所有数学运算。整数运算在芯片之间是100%一致的。或者(2)要求所有客户端使用与您开发相同的硬件。
请注意,如果您选择(2),则可能仍然会遇到问题。小细节,例如编译程序时是否启用了调试模式,可以改变浮点计算是否以额外的精度进行。这可能导致调试和发布版本之间的不一致结果,这也是意外和令人困惑的。如果您对一致性的要求比速度的要求更重要,则必须实现自己的浮点库,该库在整数中执行所有计算。
我在调试和发布模式下尝试了x86和x64:
x86 debug: Double.MaxValue
x64 debug: Infinity
x86 release: Infinity
x64 release: Infinity
看起来只有在调试模式下才会得到那个结果。
不确定为什么会有差异,但调试模式下x86代码如下:
double r = Math.Sqrt(v1 * v1);
00025bda fld qword ptr [ebp-44h]
00025bdd fmul st,st(0)
00025bdf fsqrt
00025be1 fstp qword ptr [ebp-5Ch]
00025be4 fld qword ptr [ebp-5Ch]
00025be7 fstp qword ptr [ebp-4Ch]
在发布模式下,代码与此相同:
double r = Math.Sqrt(v1 * v1);
00000027 fld qword ptr [ebp-8]
0000002a fmul st,st(0)
0000002c fsqrt
0000002e fstp qword ptr [ebp-18h]
00000031 fld qword ptr [ebp-18h]
00000034 fstp qword ptr [ebp-10h]
double.MaxValue * double.MaxValue
会导致溢出。
你应该避免计算溢出,而不是依赖于你所报告的32位行为(正如评论中所述,这似乎不太可能)。
[32位和64位版本是否具有相同的配置和设置?]
+inf
的结果。 - Joey