在.net 4.5.2中对Math.Exp或双重实现的更改

Fer*_*own 24 c# precision .net-4.5.2

如果我运行声明

Math.Exp(113.62826122038274).ToString("R")
Run Code Online (Sandbox Code Playgroud)

在安装了.net 4.5.1的机器上,然后我得到答案

2.2290860617259248E+49
Run Code Online (Sandbox Code Playgroud)

但是,如果我在安装了.net framework 4.5.2的机器上运行相同的命令,那么我得到答案

2.2290860617259246E+49
Run Code Online (Sandbox Code Playgroud)

(即最后的数字变化)

我意识到这在纯数字术语中是无关紧要的,但有没有人知道在.net 4.5.2中所做的任何可以解释变化的变化?

(我不喜欢另一个结果,我只是想了解它为什么会改变)

如果我输出

The input in roundtrip format
The input converted to a long via BitConverter.DoubleToInt64Bits
Math.Exp in roundtrip format
Math.Exp converted to a long via BitConverter.DoubleToInt64Bits
Run Code Online (Sandbox Code Playgroud)

然后在4.5.1我得到

113.62826122038274
4637696294982039780
2.2290860617259248E+49
5345351685623826106
Run Code Online (Sandbox Code Playgroud)

在4.5.2我得到:

113.62826122038274
4637696294982039780
2.2290860617259246E+49
5345351685623826105
Run Code Online (Sandbox Code Playgroud)

所以对于完全相同的输入,我得到一个不同的输出(从位可以看出,因此不涉及往返格式)

更多细节:

使用VS2015编译一次

我运行二进制文件的两台机器都是64位

一个安装了.net 4.5.1,另一个安装了4.5.2

为了清楚起见:字符串转换无关紧要......无论是否涉及字符串转换,我都会得到结果的更改.我提到这纯粹是为了证明这种变化.

Han*_*ant 12

叹了口气,浮点数学的奥秘继续让程序员永远陷入困境.它与框架版本没有任何关系.相关设置是项目>属性>构建选项卡.

平台目标= x86:2.2290860617259248E+49
平台目标= AnyCPU或x64:2.2290860617259246E+49

如果在32位操作系统上运行该程序,则始终获得第一个结果.请注意,往返格式是过度指定的,它包含的数字比双重存储的数字多.将它们计算在内,得到16.这确保了double,1和0 的二进制表示是相同的.两个值之间的差异是尾数中的最低有效位.

LSB不相同的原因是因为x86抖动阻碍了FPU的生成代码.与double可以存储相比,它具有使用更多精度的非常不理想的特性.80位而不是64位.理论上可以生成更准确的计算结果.它做了什么,但很少以可重复的方式.代码的微小变化会在计算结果中产生很大的变化.只运行附带调试器的代码可以更改结果,因为这会禁用优化器.

英特尔用SSE2指令集修复了这个错误,完全取代了FPU的浮点数学指令.它不使用额外的精度,double总是有64位.由于计算结果现在不再依赖于中间存储的非常理想的属性,现在它更加一致.但不太准确.

x86抖动使用FPU指令是一个历史性的事故.在2002年发布,没有足够的处理器支持SSE2.由于它改变了程序的可观察行为,因此无法再修复该事故.对于x64抖动来说这不是问题,64位处理器也保证也支持SSE2.

32位进程使用使用FPU代码的exp()函数.64位进程使用使用SSE代码的exp()函数.结果可能因LSB而异.但仍然准确到15位有效数字,这是2.229086061725925E + 49.所有你可以期待的数学与双重.


小智 7

.NET使用CRT中的数学库函数来进行这些计算..NET使用的CRT通常会随每个版本一起更新,因此您可以预期.NET版本之间的结果会发生变化,但是,它们总是在承诺的+/1ulp内.