2

我需要使用python来计算形式对象的对数

log( 1 - n0 - n1*1j)

其中 n0 和 n1 是非常小的数字~ 1.0e-27 和 1j 是虚数。

使用 cmath.log 给出了错误的答案

print cmath.log( 1.0 -  1e-27 - 1.0e-27j )
(5e-55-1e-27j)

使用 mpmath 我可以获得正确的结果,但前提是我正确地表达了论点

import mpmath as mp
mp.prec = 100
print mp.log(   mp.mpc(1.0) -  mp.mpc(0.00000000000000000000000001)  - mp.mpc(real='0.0', imag='1.0e-27') )

(-1.0000389695486766657204483072e-26 - 1.00000000000000000000000001e-27j)

(这是正确答案)而

print mp.log(  mp.mpc(0.99999999999999999999999999)  - mp.mpc(real='0.0', imag='1.0e-27') )

(5.0e-55 - 1.0e-27j)

这里发生了什么?我可以仅使用 cmath.log() 得到正确答案吗?

4

2 回答 2

3

Python 使用 IEEE binary64 标准(通常称为双精度)来表示浮点数。binary64 只有大约 15 个十进制数字的精度;超过 15 位的数字无法准确表示。正如你在这里看到的:

>>> 1 - 1e-27
1.0

精度损失导致1 - 1e-27四舍五入1.0。更复杂的数学库提供了计算比您输入的数字多 1 的对数的函数。例如,在numpy中:

>>> numpy.log1p(-1e-27-1e-27j)
-1e-27j

...不幸的是,这也是错误的。这让我很惊讶。我不确定为什么会这样。下一个最好的选择可能是使用像 mpmath 这样的任意精度数学库。确保使用字符串而不是浮点数来初始化任意精度的数字。如果您使用浮点文字,在任意精度库开始发挥作用之前,您将损失大量精度。

于 2014-01-16T11:29:12.717 回答
1

双精度格式大约有 15 个十进制数字。cmath.log()在您的情况下会失去精度,因为它在幅度数(1. 和您的小数)上的总和差异太大。

如果您的算法不需要高速,您可以使用泰勒级数log(1+x)。IE:

log(1+x) = x - x**2/2 + x**3/3 + ...

为了更精确,您可以实现Kahan summation algorithm

您还可以使用以下公式:

log(1+x) = math.log1p(x) + 1j*cmath.phase(1+x),

wheremath.log1p为小 x 精确计算对数。

于 2014-01-16T12:02:03.273 回答