数学的变化.Exp或double在.net 4.5.2中的实现
本文关键字:实现 net double 变化 Exp | 更新日期: 2023-09-27 18:17:57
如果我运行语句
Math.Exp(113.62826122038274).ToString("R")
在一台安装了。net 4.5.1的机器上,然后我得到答案
2.2290860617259248E+49
然而,如果我在一台安装了。net framework 4.5.2的机器上运行相同的命令,那么我得到的答案是
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,另一个安装了4.5.2
只是为了清楚:字符串转换是无关的…无论是否涉及字符串转换,我都会得到结果的变化。我提到这个纯粹是为了展示变化。
唉,浮点数学的奥秘永远困扰着程序员。它与框架版本没有任何关系。相关的设置是Project> Properties> Build选项卡。
目标平台= x86: 2.2290860617259248E+49
平台目标= AnyCPU或x64: 2.2290860617259246E+49
如果你在32位操作系统上运行程序,那么你总是得到第一个结果。注意,往返格式被过度指定了,它包含的数字比双精度类型所能存储的数字多。也就是15。数一数,一共有16个。这确保了双精度数的二进制表示、1和0是相同的。两个值之间的差值为尾数中最低有效位。
LSB不相同的原因是因为x86抖动受到为FPU生成代码的阻碍。它有一个非常不受欢迎的特性,即使用的位的精度超过了双精度型可存储的位数。80位而不是64位。理论上可以产生更准确的计算结果。它确实如此,但很少以可复制的方式。代码的微小变化会对计算结果产生很大的变化。仅仅运行带有调试器的代码就可以改变结果,因为这会禁用优化器。
英特尔用SSE2指令集修复了这个错误,完全取代了FPU的浮点数学指令。它不使用额外的精度,双精度类型总是64位。由于计算结果现在不再依赖于中间存储这一非常理想的特性,它现在更加一致了。但不太准确。x86抖动使用FPU指令是一个历史事故。在2002年发布的时候,没有足够的处理器支持SSE2。这个意外不能再修复了,因为它改变了程序的可观察行为。对于x64抖动来说,这不是问题,64位处理器保证也支持SSE2。
32位进程使用exp()函数,该函数使用FPU代码。64位进程使用exp()函数,该函数使用SSE代码。结果可能会因一个LSB而不同。但仍然精确到15位有效数字,它是2.229086061725925E+49。所有你能期望从数学中得到的都是双。
。NET使用来自CRT的数学库函数来进行这些计算。. net使用的CRT经常在每个版本中更新,因此您可以预期结果在。net版本之间会发生变化,但是,它们总是在承诺的+/1ulp范围内。
我遇到了同样的问题,但是我只在安装。net 4.6后得到它。
. net 4.6安装升级c:'windows'system32'msvcr120_clr0400.dll和c:'windows'syswow64'msvc120_clr0400.dll。
在安装这些dll之前有文件属性->详细信息:"文件版本12.0.51689.34249"answers"产品名称Microsoft Visual Studio 12 CTP"
在安装。net 4.6之后,这些显示了"文件版本12.0.52512.0"answers"产品名称Microsoft Visual Studio 2013"
我调整了我的测试,以包括您的示例,我通过在此DLL的版本之间翻转看到与您相同的之前/之后数字。
(我们的测试套件在。net版本4、4.5、4.5.1或4.5.2上运行时没有显示任何更改的结果,直到这些dll更新。)
为了展示针对不同。net版本的项目如何影响字符串的双重转换,我构建了4个针对不同版本的项目,它们都运行在使用。net 4.6的同一台开发机器上。
下面是代码
double foo = Convert.ToDouble("33.94140881672595");
这里是输出
(33.941408816725954。净4)
(33.941408816725946。NET 4.5)
(33.941408816725946。净4.5.2)
(33.941408816725946。NET 4.6)
所以,在。net 4之后,转换方法肯定会有变化