pythonpython-2.7floating-pointbigfloat

Bigfloat - Error in the precision


I'm trying to use Bigfloat library in python 2.7.

from bigfloat import *

f1 = Context(precision=2000)

with precision(2000):  f1 = 1e-19*1e-19*9e9/((1-1e-18)*(1-1e-18))-1e-19*1e-19*9e9

with precision(100):  f2 = 1.6e-27*1.6e-27*6.6e-11/(1e-18*1e-18)

print BigFloat(f1) print f2

Python gives me f1=0, but it is not true. I tested it with g++ and the result is 1.75e-46.

Is it an error in my program? Is it not possible to calculate this precision with Bigfloat ? Is it an error in the lib?


Solution

  • As an example, here's how you might compute f1 to a precision of 256 bits using the bigfloat library.

    >>> from bigfloat import BigFloat, precision
    >>> with precision(256):
    ...     x = BigFloat('1e-19')
    ...     y = BigFloat('9e9')
    ...     z = BigFloat('1e-18')
    ...     f1 = x * x * y / ((1 - z) * (1 - z)) - x * x * y
    ... 
    >>> f1
    BigFloat.exact('1.800000000000000002700000000000000003600000000000000004500006811997284879750608e-46', precision=256)
    

    Note the use of BigFloat('1e-19'), which is creating the closest binary float to 10**-19 at the current precision (256 bits). This is different from BigFloat(1e-19) (without the single quotes), since there 1e-19 is a Python float, so has already been rounded to 53-bit precision.

    Take a look at the documentation for more details.

    However, with a bit of creativity and algebra, you don't need a high-precision library at all here. You can rewrite the expression for f1 as:

    f1 = x * x * y * (1 / ((1 - z) * (1 - z)) - 1)
    

    and by putting everything over a common denominator, the quantity in parentheses can be rewritten as (2 - z) * z / ((1 - z) * (1 - z)). So you could equally well compute f1 as:

    f1 = x * x * y * (2-z) * z / ((1 - z) * (1 - z))
    

    and in this form, you don't lose accuracy when z is very small. So now regular Python floats are good enough:

    >>> x = 1e-19
    >>> y = 9e9
    >>> z = 1e-18
    >>> f1 = x * x * y * (2 - z) * z / ((1 - z) * (1 - z))
    >>> f1
    1.8e-46
    

    If you do decide you want to use a high-precision floating-point library, I'd also recommend looking at the gmpy2 library. It's based on the same underlying MPFR library as bigfloat, but it's better maintained.