I need to use python to calculate the logarithms of objects of the form
log( 1 - n0 - n1*1j)
where n0 and n1 are very small numbers ~ 1.0e-27 and 1j is the the imaginary number.
Using cmath.log gives the wrong answer
print cmath.log( 1.0 - 1e-27 - 1.0e-27j )
(5e-55-1e-27j)
Using mpmath I can obtain the correct result but only if I phrase the argument correctly
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') )
gives
(-1.0000389695486766657204483072e-26 - 1.00000000000000000000000001e-27j)
(which is the correct answer) whereas
print mp.log( mp.mpc(0.99999999999999999999999999) - mp.mpc(real='0.0', imag='1.0e-27') )
gives
(5.0e-55 - 1.0e-27j)
What's going on here? Can I get the right answer just using cmath.log()?
Python uses the IEEE binary64 standard, commonly referred to as double precision, to represent floating-point numbers. binary64 only has about 15 decimal digits worth of precision; numbers that take more than 15 digits can't be accurately represented. As you can see here:
Precision loss causes
1 - 1e-27
to round to just1.0
. More sophisticated math libraries offer functions to compute the logarithm of 1 more than the number you input. For example, in numpy:...which unfortunately is also wrong. That surprised me. I'm not sure why that happened. The next best bet would probably be to use an arbitrary-precision math library like mpmath. Make sure to initialize your arbitrary-precision numbers with strings, rather than floats. If you use floating-point literals, you'll lose a ton of precision before the arbitrary-precision library comes into play.