cpython

时间:2018-02-06 13:56:55

标签: python cpython

我在git(第25行)中查看_math.c

#if !defined(HAVE_ACOSH) || !defined(HAVE_ASINH)
static const double ln2 = 6.93147180559945286227E-01;
static const double two_pow_p28 = 268435456.0; /* 2**28 */

我注意到ln2值与ln2的wolframalpha值不同。 (光头部分是差异)

ln2 = 0.693147180559945 286227 (cpython)

ln2 = 0.693147180559945 3094172321214581 (wolframalpha)

ln2 = 0.693147180559945 309417232121458 (维基百科)

所以我的问题是为什么会有区别?我错过了什么?

3 个答案:

答案 0 :(得分:6)

如user2357112所述,此代码来自FDLIBM。这是为IEEE-754机器精心编写的,其中C双精度具有53位精度。它并不真正关心2的实际日志是什么,但关注log(2)的最佳53位近似值。

要重现预期的53位精确值17 decimal digits would have sufficed

那为什么他们使用21位十进制数字呢?我的猜测:21位十进制数是保证转换结果正确到64位精度所需的最小值。如果编译器以某种方式决定将文字转换为Pentium的80位浮点格式(具有64位精度),那么可能在当时是一个问题。

所以他们用足够的十进制数字显示53位结果,这样如果它被转换为具有64位精度的二进制浮点格式,则尾随11位(= 64-53)将所有都是零,从而确保他们从一开始就使用他们想要的53位值。

>>> import mpmath
>>> x = mpmath.log(2)
>>> x
mpf('0.69314718055994529')
>>> mpmath.mp.prec = 64
>>> y = mpmath.mpf("0.693147180559945286227")
>>> x == y
True
>>> y
mpf('0.693147180559945286227')

在英语中,xlog(2)的53位精确值,y是将代码中的十进制字符串转换为64位二进制浮点格式的结果精度它们完全相同。

在当前的现实中,我希望所有编译器现在将文字转换为本机IEEE-754双格式,精度为53位。

无论哪种方式,代码都会确保使用log(2)的最佳53位近似值。

答案 1 :(得分:4)

直到binary64浮点表示的精度,这些值是相等的:

In [21]: 0.6931471805599453094172321214581 == 0.693147180559945286227
Out[21]: True
如果将最准确的可表示的ln(2)近似值存储到64位浮点数中然后将其打印到那么多位数,那么

0.693147180559945286227就是你得到的。试图在float中填充更多数字只会将结果四舍五入为相同的值:

In [23]: '%.21f' % 0.6931471805599453094172321214581
Out[23]: '0.693147180559945286227'

至于为什么他们在代码中写了0.693147180559945286227,你不得不问那些在1993年在Sun写过FDLIBM的人。这段代码来自FDLIBM。

答案 2 :(得分:3)

Python似乎错了,虽然我不确定它是一种疏忽还是它有更深的含义。对BlackJack的解释似乎是合理的,但我不明白,为什么他们会给出其他错误的数字。

您可以使用More efficient series下的公式自行检查。在Mathematica中,您可以使用

计算最多70(35个加数)
log2 = 2*Sum[1/i*(1/3)^i, {i, 1, 70, 2}]

(*
79535292197135923776615186805136682215642574454974413288086/
114745171628462663795273979107442710223059517312975273318225
*)

使用N[log2,30],您可以获得正确的数字

0.693147180559945309417232121458

支持维基百科和W | A的正确性。如果您愿意,可以对机器精度数进行相同的计算。在Mathematica中,这通常意味着double

logC = Compile[{{z, _Real, 0}},
  2.0*Sum[1/i*((z - 1)/(z + 1))^i, {i, 1, 100, 2}]
]

请注意,此代码完全编译为正常迭代,并且不使用某些错误减少求和方案。所以没有神奇的编译Sum函数。这在我的机器上显示:

logC[2]//FullForm
(* 0.6931471805599451` *)

并且在您指出的数字之前是正确的。这具有BlackJack建议的精度

$MachinePrecision
(* 15.9546 *)

修改

正如评论和答案中所指出的,您在_math.c中看到的值可能是53位表示

digits = RealDigits[log2, 2, 53];
N[FromDigits[digits, 2], 21]

(*  0.693147180559945286227 *)