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