gmpy2 log2 16 位后不准确

gmpy2 log2 not accurate after 16 digits

在 gmpy2 中使用 log2() 时,16 位数字后似乎不准确。它似乎在 15 位数时工作正常,但之后使用 mpz(mpfr(2) ** mpfr(x)) 答案不正确。我需要更改精度吗?我认为 python 本身最多可以精确到 53 位数字。

另外,除了10和2之外,gmpy2有没有办法在基数上使用对数运算?例如,基数 8 或 16。

标准的Python浮点类型精确到53位,大约是16位十进制数字。 gmpy2 使用 53 位的默认精度。如果您想要更准确的结果,则需要提高精度。

>>> import gmpy2
>>> from gmpy2 import mpz,mpfr,log2
>>> a=12345678901234567890
>>> gmpy2.get_context().precision=70
>>> mpz(2**log2(a))
mpz(12345678901234567890L)

要计算不同的对数,只需使用

>>> gmpy2.log(x)/gmpy2.log(base)

更新

从一系列浮点计算中恢复精确的整数结果通常是不可能的。根据实际计算,可以提高精度直到得到"close enough".

让我们看看精度的影响。请注意,a 是 57 位长,因此不能用 53 位浮点精度精确表示。

>>> a=123543221556677776
>>> a.bit_length()
57
>>> gmpy2.get_context().precision=53
>>> mpfr(a);2**log2(a)
mpfr('1.2354322155667778e+17')
mpfr('1.2354322155667752e+17')

由于将二进制浮点数转换为十进制可能会引入转换错误,让我们以二进制形式查看结果。

>>> mpfr(a).digits(2);(2**log2(a)).digits(2)
('11011011011101001111001111100101101001011000011001001', 57, 53)
('11011011011101001111001111100101101001011000010111001', 57, 53)

让我们尝试将精度提高到 57 位。

>>> gmpy2.get_context().precision=57
>>> mpfr(a).digits(2);(2**log2(a)).digits(2)
('110110110111010011110011111001011010010110000110010010000', 57, 57)
('110110110111010011110011111001011010010110000110010011000', 57, 57)

注意更多位是正确的,但仍然有错误。让我们试试 64 位。

>>> gmpy2.get_context().precision=64
>>> mpfr(a);2**log2(a)
mpfr('123543221556677776.0',64)
mpfr('123543221556677775.953',64)
>>> mpfr(a).digits(2);(2**log2(a)).digits(2)
('1101101101110100111100111110010110100101100001100100100000000000', 57, 64)
('1101101101110100111100111110010110100101100001100100011111111010', 57, 64)

大量的尾随 1 大致相当于十进制的尾随 9。

一旦你得到 "close enough",你可以转换为一个整数,将结果四舍五入到预期值。

为什么 57 位不够? gmpy2 使用的 MPFR 库确实执行了正确的舍入。还有一个小错误。我们还可以使用恰好位于正确舍入值上方和下方的浮点值来查看结果。

>>> gmpy2.get_context().precision=57
>>> b=log2(a)
>>> 2**gmpy2.next_below(b);2**log2(a);2**gmpy2.next_above(b)
mpfr('123543221556677746.0',57)
mpfr('123543221556677784.0',57)
mpfr('123543221556677822.0',57)

请注意,即使 b 的微小变化也会导致 2**b 的巨大变化。

更新 2

浮点运算只是实数数学属性的近似值。有些数字是有理数(它们可以写成分数),但大多数数字是无理数(它们永远不能准确地写成分数)。浮点运算实际上使用对数字的有理逼近。

我在下面跳过了一些细节 - 我假设所有数字都在 0 和 1 之间。

对于二进制浮点数(大多数计算机使用的),有理逼近的分母必须是 2 的幂。像 1/21/4 这样的数字可以精确表示。十进制浮点数使用分母为 10 的幂的有理近似值。1/2、'1/4'、'1/5' 和 1/20 等数字都可以精确表示。两者都不能准确表示 1/3。浮点运算的 base-6 实现可以准确表示 1/21/3,但不能表示 1/10。特定格式的精度仅指定分子的最大大小。总会有一些有理数不能用给定的基数精确表示。

由于无理数不能写成有理数,因此不能用给定的底数准确表示它们。由于对数和指数函数几乎总是产生无理数,因此计算几乎从不准确。通过提高精度,您通常可以获得 "close enough" 但您永远无法获得准确的结果。

有些程序可以工作 symbolically - 他们记得 alog2(n) 并且当您执行 2**a 时,a 的确切值是回来。参见 SymPy