python logarithm错误地计算了非常小的复数

时间:2014-01-16 11:11:57

标签: python complex-numbers logarithm mpmath natural-logarithm

我需要使用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()获得正确的答案吗?

2 个答案:

答案 0 :(得分: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这样的任意精度数学库。确保使用字符串而不是浮点数初始化任意精度数字。如果使用浮点文字,在任意精度库发挥作用之前,你将失去很多精度。

答案 1 :(得分:1)

双格式精度约为15位小数。 cmath.log()在你的情况下会失去精确度,因为它的数量总和(1.和你的小数字)总和太大了。

如果您的算法不需要高速,则可以使用泰勒系列log(1+x)。即:

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

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

您还可以使用以下公式:

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

其中math.log1p精确计算小x的对数。