C#-32位和64位上不一致的数学运算结果

C#-32位和64位上不一致的数学运算结果,c#,C#,考虑以下代码: double v1 = double.MaxValue; double r = Math.Sqrt(v1 * v1); r=32位机器上的double.MaxValue r=64位机器上的无穷大 我们在32位机器上开发,因此在客户通知之前不知道问题。为什么会出现这种不一致?如何防止这种情况发生?问题在于需要一个double-as参数v1*v1不能存储为双精度和溢出,从而导致未定义的行为。double.MaxValue*double.MaxValue是溢出 您应该避免计算溢出,而

考虑以下代码:

double v1 = double.MaxValue;
double r = Math.Sqrt(v1 * v1);
r=32位机器上的double.MaxValue r=64位机器上的无穷大


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

问题在于需要一个double-as参数
v1*v1
不能存储为双精度和溢出,从而导致未定义的行为。

double.MaxValue*double.MaxValue
是溢出

您应该避免计算溢出,而不是依赖于您报告的32位行为(正如上面所评论的,这似乎不关键)


[32位和64位构建是否具有相同的配置和设置?]

我在调试和发布模式下在x86和x64中尝试了这一点:

x86 debug:   Double.MaxValue
x64 debug:   Infinity
x86 release: Infinity
x64 release: Infinity
            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]
所以,似乎只有在调试模式下才能得到结果

但不确定为什么会有差异,调试模式下的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] 
与释放模式下的代码相同:

x86 debug:   Double.MaxValue
x64 debug:   Infinity
x86 release: Infinity
x64 release: Infinity
            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]

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

这在x64 JIT编译器中得到了修复,它使用SSE指令,SSE寄存器的大小与双精度寄存器相同

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

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

Q&D修复程序是Project+Properties,Compile选项卡,platformtarget=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的值时会触发。这是一个bug,您可以在connect.microsoft.com上报告它。不过,我很确定他们不会修复它。

这几乎是一个复制品

我对这个问题的回答也回答了这个问题。简而言之:不同的硬件可以根据硬件的细节给出或多或少的准确结果

如何防止这种情况发生?因为问题在芯片上,你有两个选择。(1) 不要用浮点数做任何数学运算。你所有的数学都是整数。整数数学在芯片之间是100%一致的。或者(2)要求所有客户使用与您开发相同的硬件


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

在32位和64位机器上产生
+inf
。IEEE 754没有未定义的行为。溢出的
double
被定义为正无穷大,并且如何使用它进行进一步操作的规则非常清楚。您不是在看JIT优化的代码。工具+选项,调试,取消选中“抑制JIT优化”。您将看到抖动并不影响生成fmul指令。这是一个bug。不幸的是,我们需要程序在64位Windows上以纯64位运行。这是一个工程应用,所有的数学都是整数是不切实际的。