为什么Math.Exp在32位和64位之间给出不同的结果,具有相同的输入,相同的硬件

时间:2010-10-25 21:10:58

标签: c# .net math 32bit-64bit x86-64

我正在使用带有PlatformTarget x64和x86的.NET 2.0。我给Math.Exp输入了相同的输入数字,它在任一平台上返回不同的结果。

MSDN表示你不能依赖文字/解析的Double来表示平台之间的相同数字,但我认为我在下面使用Int64BitsToDouble可以避免这个问题,并保证在两个平台上输入相同的Math.Exp。

我的问题是为什么结果不同?我原以为:

  • 输入以相同的方式存储(双/ 64位精度)
  • 无论处理器的位数如何,FPU都会进行相同的计算
  • 输出以相同的方式存储

我知道我不应该比较15/17位数后的浮点数,但是我对这里的不一致感到困惑,看起来在同一硬件上看起来是相同的操作。

任何人都知道引擎盖下发生了什么?

double d = BitConverter.Int64BitsToDouble(-4648784593573222648L); // same as Double.Parse("-0.0068846153846153849") but with no concern about losing digits in conversion
Debug.Assert(d.ToString("G17") == "-0.0068846153846153849"
    && BitConverter.DoubleToInt64Bits(d) == -4648784593573222648L); // true on both 32 & 64 bit

double exp = Math.Exp(d);

Console.WriteLine("{0:G17} = {1}", exp, BitConverter.DoubleToInt64Bits(exp));
// 64-bit: 0.99313902928727449 = 4607120620669726947
// 32-bit: 0.9931390292872746  = 4607120620669726948

在打开或关闭JIT的两个平台上,结果都是一致的。

[编辑]

我对下面的答案并不完全满意,所以这里有一些我的搜索细节。

http://www.manicai.net/comp/debugging/fpudiff/说:

  

因此32位使用80位FPU寄存器,64位使用128位SSE寄存器。

CLI标准表示,如果硬件支持双精度,则可以用更高的精度表示双精度:

  

[原理:此设计允许CLI为其选择特定于平台的高性能表示   浮点数,直到它们被放置在存储位置。例如,它可能会离开   硬件寄存器中的浮点变量,提供比用户请求的更高的精度。在   分区I 69   同时,CIL生成器可以强制操作遵守特定于语言的表示规则   使用转换指令。最终理由]

http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf(12.1.3处理浮点数据类型)

我认为这就是这里发生的事情,因为Double的标准15位精度后结果不同。 64位Math.Exp结果更精确(它有一个额外的数字),因为内部64位.NET使用的FPU寄存器比32位.NET使用的FPU寄存器更精确。

2 个答案:

答案 0 :(得分:4)

是舍入错误,它实际上不是相同的硬件。 32位版本针对的是一组不同的指令和寄存器大小。

答案 1 :(得分:3)

使用Double类型,您将获得舍入错误,因为二进制中的分数会非常快地变得非常大。如果您使用Decimal类型,它可能会有所帮助。