2010-03-17 256 views
11

考虑下面的代码不一致数学运算的结果:C# - 在32位和64位

double v1 = double.MaxValue; 
double r = Math.Sqrt(v1 * v1); 

R = double.MaxValue在32位机器 R = 64位机器上无限

我们在32位机器上开发,因此不知道这个问题,直到客户通知。为什么会出现这种不一致?如何防止这种情况发生?

+2

这里在32位和64位机器上都会产生'+ inf'。 – Joey 2010-03-17 10:12:27

回答

21

x86指令集有由于FPU的工作方式而引起的棘手的浮点一致性问题。内部计算的执行位数高于可存储在double中的位数,当数字从FPU堆栈刷新到内存时导致截断。

在x64 JIT编译器中得到修复,它使用SSE指令,SSE寄存器与double有相同的大小。

当您的计算测试浮点精度和范围的边界时,这将以字节为单位。你永远不想接近需要超过15位有效数字,你永远不想接近10E308或10E-308。你当然不希望划分最大的可表示价值。这绝不是一个真正的问题,代表物理量的数字不会接近。

利用这个机会找出你的计算出了什么问题。运行客户正在使用的相同操作系统和硬件非常重要,您需要很长时间才能获得所需的机器。仅在x86机器上测试的运输代码未经测试。

Q & D修复程序是Project + Properties,Compile选项卡,Platform Target = x86。


Fwiw,x86上的错误结果是由JIT编译器中的错误引起的。它生成此代码:

 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上报告它。很确定他们不会修复它。

+0

“字节你” - 故意双关或错字? ;) – 2010-03-17 10:50:48

+7

@Jonners:非常有意。 – 2010-03-17 10:57:03

1

问题是,Math.Sqrt期望双重作为参数。 v1 * v1不能存储为双精度且溢出 导致未定义的行为

+1

IEEE 754没有未定义的行为。溢出的'double'被定义为正无穷,并且规则如何进行进一步的操作是非常清楚的。 – Joey 2010-03-17 10:13:41

+0

@Johannes,谢谢你的澄清。 – 2010-03-17 10:14:39

1

double.MaxValue * double.MaxValue是溢出。

您应该避免计算溢出,而不是依赖于您报告的32位行为(正如评论似乎不喜欢)。

[是32位和64位建立相同的配置和设置?]

+0

完全一样。 – 2010-03-18 07:23:19

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] 
+0

你没有看JIT优化的代码。工具+选项,调试,取消选中“取消JIT优化”。你会看到JITter不会生成fmul指令。这是一个错误。 – 2010-03-17 10:34:17

+0

@nobugz:我试过了,但代码仍然是一样的。 – Guffa 2010-03-17 10:45:37

3

这是

Why does this floating-point calculation give different results on different machines?

我回答这个问题,还回答了这一个近了重复。总之:根据硬件的细节,允许不同的硬件提供更多或更少的准确结果。

如何防止它发生?由于问题出在芯片上,你有两种选择。 (1)不要用浮点数做任何数学运算。用整数完成所有数学。整数数学从芯片到芯片是100%一致的。或者(2)要求所有客户在开发时使用相同的硬件。

请注意,如果您选择(2),那么你可能仍然有问题;例如程序是否被编译调试或零售这些小细节可以改变浮点计算是否以更高的精度完成。这可能会导致调试和零售版本之间的结果不一致,这也是意外和令人困惑的。如果你的一致性要求比你的速度要求更重要,那么你将不得不实现你自己的浮点库来完成所有的整数计算。

+0

不幸的是,我们需要该程序在64位Windows上以纯64位运行。这是一个工程应用,在整数中进行所有的数学计算是不切实际的。 – 2010-03-18 07:35:03