以下C程序在我的Mac和Linux上产生不同的结果。我很惊讶,因为我认为libm
的实现在某种程度上是标准化的
#include<math.h>
#include<stdio.h>
int main()
{
double x18=-6.899495205106946e+01;
double x19 = exp(-x18);
printf("x19 = %.15e\n", x19);
printf("x19 hex = %llx\n", *((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结果也会关闭。
答案 0 :(得分:2)
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。
答案 1 :(得分:1)
在二进制级别指定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
做出贡献。