Mac和Linux上的exp函数结果稍有不同
以下C程序在我的Mac和Linux上产生不同的结果。 我很惊讶,因为我认为libm
的实现标准化
#include<math.h>
#include<stdio.h>
int main()
{
double x18=-6.899495205106946e+01;
double x19 = exp(-x18);
printf("x19 = %.15en", x19);
printf("x19 hex = %llxn", *((unsigned long long *)(&x19)));
}
Mac上的输出是
x19 = 9.207186811339878e+29
x19 hex = 46273e0149095886
和Linux上
x19 = 9.207186811339876e+29
x19 hex = 46273e0149095885
两者都没有任何优化标记,如下所示:
gcc -lm ....
我知道我永远不应该比较漂亮的花车。
这个问题出现在调试过程中,令人遗憾的是使用这种计算证明的算法在数值上不稳定,而这种微小的差异导致最终结果的显着偏差。 但这是一个不同的问题。
我很惊讶像exp
这样的基本操作不是标准化的,因为我可以期待IEEE 754规定的基本代数操作。
对于不同机器或不同版本的libm
不同实现,我是否可以依赖精度的假设?
由于下面的讨论,我用mpmath
来计算高于机器精度的值,并且我得到了两个更多的数字9.2071868113398768244
,所以对于我的两个结果来说最后一个数字已经是错误的了。 在Linux上的结果可以通过舍入这个值来解释,如果计算机使用四舍五入,Mac的结果也是关闭的。
C99规格说明(其他版本应该类似):
J.3实现定义的行为
1需要遵循一致的实现来记录其在本子条款列出的每个区域中的行为选择。 以下是实现定义的:
...
J.3.6浮点
- 浮点运算和<math.h>
和<complex.h>
中返回浮点结果(5.2.4.2.2)的库函数的精度。
含义GNU libm和BSD libm可以自由地具有不同级别的准确性。 可能发生的事情是,OSX上的BSD实现向最近的单元(最后一个单元)ULP舍去,并且GNU实现截断为下一个ULP。
IEEE-754行为在二进制级别指定。 使用Linux,我为Python的本地math
库, mpmath
和MPFR(通过gmpy2
)获得相同的值。 但是,在三种方法之间转换为小数。
>>> import mpmath, gmpy2
>>> import mpmath, gmpy2, math
>>> x18=68.99495205106946
>>> x19=math.exp(x18)
>>> mp18=mpmath.mpf("68.99495205106946")
>>> mp19=mpmath.exp(mp18)
>>> gp18=gmpy2.mpfr("68.99495205106946")
>>> gp19=gmpy2.exp(gp18)
>>> x18 == mp18
True
>>> x18 == gp18
True
>>> x19 == mp19
True
>>> x19 == gp19
True
>>> print(x18, mp18, gp18)
68.99495205106946 68.9949520510695 68.994952051069461
>>> print(x19, mp19, gp19)
9.207186811339876e+29 9.20718681133988e+29 9.2071868113398761e+29
在转换为Python的任意精度整数形式之后,所有三个结果也显示为精确。
>>> hex(int(x19))
'0xb9f00a484ac42800000000000'
>>> hex(int(mp19))
'0xb9f00a484ac42800000000000'
>>> hex(int(gp19))
'0xb9f00a484ac42800000000000'
所以(至少有一个)Linux数学库mpmath
和gmpy2.mpfr
同意。
免责声明:我保留gmpy2
并在过去为mpmath
做出了贡献。
上一篇: Slightly different result from exp function on Mac and Linux
下一篇: firebase local server > how to refresh browser when files changed