【发布时间】:2011-04-30 10:52:52
【问题描述】:
我正在使用带有 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 寄存器精度更高。
【问题讨论】:
-
+1 有趣。我在我的机器上看到完全相同的症状,并且在 x86/anycpu 之间切换会改变输出。
-
您的最后一段不正确。 32 位版本会更正确,因为它使用 80 位扩展精度 x87 FPU,而 64 位版本将使用更快、更一致的 SSE2。
-
这么多重复:C# - Inconsistent math operation result on 32-bit and 64-bit、Why would the same code yield different numeric results on 32 vs 64-bit machines?、Floating point calculation change depending on the compiler、Why does this floating-point calculation give different results on different machines?、Floating point mismatch between compilers
标签: c# .net math 32bit-64bit x86-64