C# 在.NET4.5.2中对Math.Exp或double实现的更改
如果我运行语句C# 在.NET4.5.2中对Math.Exp或double实现的更改,c#,precision,.net-4.5.2,C#,Precision,.net 4.5.2,如果我运行语句 Math.Exp(113.62826122038274).ToString("R") 在安装了.NET4.5.1的机器上,我得到了答案 2.2290860617259248E+49 2.2290860617259246E+49 但是,如果我在安装了.net framework 4.5.2的计算机上运行相同的命令,那么我会得到答案 2.2290860617259248E+49 2.2290860617259246E+49 (即最后的数字变化) 我意识到,从纯数字的角度来看
Math.Exp(113.62826122038274).ToString("R")
在安装了.NET4.5.1的机器上,我得到了答案
2.2290860617259248E+49
2.2290860617259246E+49
但是,如果我在安装了.net framework 4.5.2的计算机上运行相同的命令,那么我会得到答案
2.2290860617259248E+49
2.2290860617259246E+49
(即最后的数字变化)
我意识到,从纯数字的角度来看,这基本上是无关紧要的,但有人知道.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,另一个安装了.net 4.5.2
为了清楚起见:字符串转换是不相关的。。。无论是否涉及字符串转换,我都会得到结果的变化。我提到这一点纯粹是为了证明这种变化 .NET使用CRT中的数学库函数进行这些计算。.NET使用的CRT通常随每个版本更新,因此您可以预期.NET版本之间的结果会发生变化,但是,它们始终在承诺的+/1ulp范围内。我遇到了相同的问题,但我只有在安装.NET 4.6后才得到它 .Net 4.6安装升级了c:\windows\system32\msvcr120\u clr0400.dll和c:\windows\syswow64\msvc120\u clr0400.dll 在安装之前,这些DLL具有文件属性->详细信息:“文件版本12.0.51689.34249”和“产品名称Microsoft Visual Studio 12 CTP” 在安装.net 4.6之后,它们有“文件版本12.0.52512.0”和“产品名称Microsoft Visual Studio 2013” 我调整了测试以包含您的示例,通过在这个DLL的版本之间切换,我看到了与您相同的前后数字
(我们的测试套件在.net版本4、4.5、4.5.1或4.5.2上运行时没有显示任何更改的结果,直到这些DLL被更新。)唉,浮点数学的奥秘永远困扰着程序员。它与框架版本没有任何关系。相关设置为“项目>属性>生成”选项卡 平台目标=x86:
2.2290860617259248E+49
平台目标=任何CPU或x64:
2.2290860617259246E+49
如果在32位操作系统上运行该程序,则始终会得到第一个结果。请注意,往返格式指定过多,它包含的数字比双精度存储的数字多。这是15。数一数,你得到16分。这确保了double、1s和0s的二进制表示形式相同。两个值之间的差值是尾数中的最低有效位
LSB不一样的原因是x86抖动被为生成代码所困扰。它有一个非常不受欢迎的特性,即使用的精度比特数超过了双精度存储器所能存储的比特数。80位而不是64位。从理论上讲,可以生成更精确的计算结果。确实如此,但是。对代码的微小更改可能会对计算结果产生较大的更改。只要在附加了调试器的情况下运行代码就可以更改结果,因为这样会禁用优化器
英特尔用SSE2指令集修复了这个错误,完全取代了FPU的浮点数学指令。它不需要额外的精度,一个double总是有64位。由于计算结果现在不再依赖于中间存储这一非常理想的特性,它现在更加一致。但不太准确
x86抖动使用FPU指令是一个历史事故。在2002年发布的版本中,没有足够的处理器支持SSE2。该事故无法再修复,因为它改变了程序的可观察行为。x64抖动不是问题,64位处理器保证也支持SSE2
32位进程使用使用FPU代码的exp()函数。64位进程使用使用SSE代码的exp()函数。一个LSB的结果可能不同。但仍然精确到15位有效数字,它是2.229086061725925E+49。使用double,您可以从数学中得到所有期望。为了说明针对不同.NET版本的项目如何影响字符串的双重转换,我构建了4个针对不同版本的项目,所有这些项目都运行在使用.NET 4.6的同一台开发机器上 这是密码
double foo = Convert.ToDouble("33.94140881672595");
这是输出
33.941408816725954(.NET 4)
33.941408816725946(.净额4.5)
33.941408816725946(.NET 4.5.2)
33.941408816725946(.NET 4.6)
因此,在安装了.NET 4之后,转换方法肯定发生了变化。@AndreasMüller-不,只有4.5.2,尽管4.6与4.5.2的答案相同-这一变化似乎是由4.5.2而不是4引入的。6@WaiHaLee请参阅更新的问题浮点操作由CPU执行,而不是由框架执行。CPU之间有区别吗?这种差异也可能是由一个使用FPU的框架和另一个使用SSE的框架造成的。4.5.2没有做出这样的更改,但4.6引入了SIMD操作。4.6是4.5的二进制替换,所以这可能是导致我的机器与
4.5.2
不同的原因。我得到了你的第一个结果,最后的数字以8结尾,而不是6,2.2290806172598E+49
@flq-C Runtime。它的编译器附带了微软的C标准库。这篇文章有很多好的信息:我不相信这解释了“Math.Exp在安装.NET 4.6.1并应用KB3098785后返回不同的结果”,我告诉过你该怎么做。请自己尝试一下,更改平台目标并观察x86生成0.71612515940795685,x64生成0.716125