与Mac和Linux上的exp函数略有不同

时间:2017-06-26 17:48:31

标签: c linux macos precision numeric

以下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结果也会关闭。

2 个答案:

答案 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数学库mpmathgmpy2.mpfr同意。

免责声明:我维持gmpy2并且过去曾为mpmath做出贡献。