2015-10-15 100 views
23

如果我的机器上运行该语句更改Math.Exp或双实施.NET 4.5.2

Math.Exp(113.62826122038274).ToString("R") 

安装.NET 4.5.1,然后我得到的答案

2.2290860617259248E+49 

但是,如果我的机器上运行相同的命令与安装了.NET Framework 4.5.2,然后我得到的答案

2.2290860617259246E+49 

(即最终d igit变化)

我意识到,这是纯粹的数字大致而言微不足道,但没有人知道的已经在.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 

然后在4.5.1,我得到

113.62826122038274 
4637696294982039780 
2.2290860617259248E+49 
5345351685623826106 

和4.5.2,我得到:

113.62826122038274 
4637696294982039780 
2.2290860617259246E+49 
5345351685623826105 

因此,对于完全相同的输入,我得到一个不同的输出

更多细节(这可以从比特所以没有往返格式参与可以看出):

编译一次使用VS2015

这两款机器我正在运行的二进制文件是64位

一个已安装.NET 4.5.1,其他4.5.2

只是为了清晰:字符串转换是不相关的。我得到的结果REGA变化无论是否涉及字符串转换。我提到纯粹是为了展示变化。

+0

也安装了net46吗? – Dbl

+0

@AndreasMüller - 不,只是4.5.2,虽然4.6显示了与4.5.2相同的答案 - 更改似乎是由4.5.2引入的,而不是4.6 –

+0

您是否正在编译每台机器上的代码?如果是这样,其中一个使用Roslyn,另一个使用“旧”编译器?这可以解释事情 - 但为了减少事情,我建议删除'Math.Exp'调用,并打印出'113.62826122038274.ToString(“R”)'。如果我的怀疑是正确的,你也会看到不同的结果。 –

回答

7

.NET使用数学LIB功能从CRT做这些计算。由.NET所使用的CRT经常与各版本更新,所以你可以期待的结果,.NET版本之间发生变化,但是,他们永远是承诺+/1ulp内。

+0

什么是CRT? – flq

+0

@flq - C运行时。它的微软C编译器附带的C标准库。这个SO帖子有很多很好的信息:http:// stackoverflow。com/questions/2766233/what-is-c-runtime-library – antiduh

1

我绊倒了同样的问题,但我只安装的.Net 4.6后得到它。

.NET 4.6安装升级了c:\ windows \ system32 \ msvcr120_clr0400.dll和c:\ windows \ syswow64 \ msvc120_clr0400.dll。

安装这些DLL文件有属性 - >细节之前:“文件版本12.0.51689.34249”和“产品名称微软的Visual Studio 12 CTP”

安装.NET 4.6后,这些原本“文件版本12.0.52512.0 “和‘产品名称微软的Visual Studio 2013’

我调整我的测试,包括你的榜样,我看到相同的号码,你前/后该DLL的版本之间翻转。

(.NET的版本4,4.5运行时,我们的测试套件,并没有表现出任何改变的结果,4.5.1,4.5.2或直到这些DLL进行了更新。)

11

叹息,浮动的奥秘点数学继续为程序员树立了永恒的烙印。它与框架版本没有任何关系。相关设置是项目>属性>生成选项卡。

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

如果您运行32位操作系统上的程序,那么你总是得到的第一个结果。请注意,往返的格式是过分指定的,它包含的数字比双重罐头商店多。这是15.计数它们,你得到16.这确保了二进制表示的双倍数,1s和0s是相同的。这两个值之间的差异是尾数中的最低位。

LSB不一样的原因是因为x86抖动受the FPU生成代码的影响。这具有使用更多位的精度比双可存储的非常不利的性质。 80位而不是64位。理论上可以生成更准确的计算结果。它的确如此,但是rarely in a reproducible way。对代码的小改动会对计算结果产生较大的改变。只需使用附加的调试器运行代码就可以改变结果,因为禁用了优化器。

Intel用SSE2指令集修正了这个错误,完全取代了FPU的浮点运算指令。它不使用额外的精度,double总是有64位。由于计算结果现在不再依赖于中间存储,所以非常令人满意,现在它更加一致。但不太准确。

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

32位进程使用使用FPU代码的exp()函数。一个64位的进程使用使用SSE代码的exp()函数。结果可能会因一个LSB​​而不同。但仍精确到15位有效数字,它是2.229086061725925E + 49。所有你可以期望的数学与

+0

我不确信这解释了“Math.Exp在安装.NET 4.6.1并应用KB3098785后返回不同的结果”https://connect.microsoft .com/VisualStudio/feedback/details/2486915/math-exp-returns-different-results –

+0

我告诉过你该怎么做。请亲自尝试,更改平台目标并观察x86生成0.71612515940795685和x64生成0.71612515940795696。与框架版本无关,与FPU与SSE2相关的一切。 –

+0

在我的情况下任何CPU给了2.2290860617259248E + 49,因为首选32位被设置。但一般来说,你的答案也是一样。 – cassandrad

1

为了展示如何针对不同.NET版本的项目影响字符串的双重转换,我构建了4个项目,针对不同版本,全部运行在.NET 4.6的同一开发机器上。

下面的代码

double foo = Convert.ToDouble("33.94140881672595"); 

而这里的输出

33.941408816725954(.NET 4)

33.941408816725946(.NET 4.5)

33.941408816725946(.NET 4.5.2)

33.941408816725946(.NET 4。6)

因此,.NET 4后的转换方法肯定有变化