我正在使用带有PlatformTarget x64和x86的.NET 2.0。 我将相同的输入数字提供给Math.Exp,并且在两个平台上返回不同的结果。
MSDN说您不能依赖于文字/解析的Double来表示平台之间相同的数字,但我认为我下面使用的Int64BitsToDouble避免了这个问题,并保证了在两个平台上将相同的输入提供给Math.Exp。
我的问题是为什么结果不同? 我本来以为:
有人知道内部发生了什么吗?
MSDN说您不能依赖于文字/解析的Double来表示平台之间相同的数字,但我认为我下面使用的Int64BitsToDouble避免了这个问题,并保证了在两个平台上将相同的输入提供给Math.Exp。
我的问题是为什么结果不同? 我本来以为:
- 输入以相同的方式存储(双精度/ 64位精度)
- 无论处理器的位数如何,FPU都会执行相同的计算
- 输出以相同的方式存储
有人知道内部发生了什么吗?
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 在将浮点数放置在存储位置之前选择特定于平台的高性能表示。例如,它可能能够将浮点变量保留在提供比用户请求的更高精度的硬件寄存器中。同时,CIL 生成器可以通过使用转换指令强制操作遵守语言特定的表示规则。结束基本原理]
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf(12.1.3 处理浮点数据类型)
我认为这就是发生的事情,因为在 Double 标准的 15 位精度后,结果不同了。64 位的 Math.Exp 结果更精确(它多了一位数字),因为在内部,64 位的 .NET 使用一个比 32 位的 .NET 更精确的 FPU 寄存器。