cpython _math.c 中的 ln2 const 值

ln2 const value in _math.c in 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 的 what wolframalpha 值不同。 (光头部分是有区别的)

ln2 = 0.693147180559945286227 (cpython)

ln2 = 0.6931471805599453094172321214581 (wolframalpha)

ln2 = 0.693147180559945309417232121458(维基百科)

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

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 *)

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

In [21]: 0.6931471805599453094172321214581 == 0.693147180559945286227
Out[21]: True

0.693147180559945286227 是将 ln(2) 最准确的可表示近似值存储到 64 位浮点数中,然后将其打印成那么多数字时得到的结果。尝试在浮点数中填充更多数字只会将结果四舍五入为相同的值:

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

至于代码里为什么要写0.693147180559945286227,你得问问1993年Sun写FDLIBM的人了,这个代码来自FDLIBM

如 user2357112 所述,此代码来自 FDLIBM。这是为 IEEE-754 机器精心编写的,其中 C double 具有 53 位精度。它并不真正关心 2 的实际对数是多少,而是非常关心 log(2).

的最佳 53 位近似值

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

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

因此他们显示了具有足够十进制数字的 53 位结果,以便 if 它被转换为具有 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 位近似值。