Skip to content Skip to sidebar Skip to footer

How Can I Get More Exact Decimal Values In Python

from math import sqrt a=1e-8 b=10 c=1e-8 x1 = ((-b)-sqrt((b**2)-(4*a*c)))/(2*a) x2 = ((-b)+sqrt((b**2)-(4*a*c)))/(2*a) print 'x1 = {}'.format(x1) print 'x2 = {}'.format(x2) pr

Solution 1:

Use the decimal module:

from decimal import Decimal
a = Decimal('1E-8')
b = 10
c = Decimal('1E-8')
x1 = ((-b)-((b**2)-(4*a*c)).sqrt())/(2*a)
x2 = ((-b)+((b**2)-(4*a*c)).sqrt())/(2*a)
print 'x1 = {}'.format(x1)
print 'x2 = {}'.format(x2)

results in

x1 = -999999999.999999999000000000
x2 = -1.0000000000E-9

Solution 2:

You don't need extra precision to solve this problem: Python floats already have enough precision for the job. You just need a (slightly) cleverer algorithm.

Your problem stems from the subtraction of two nearly-equal computed values: for b positive and large (in comparison to a and c) when you do -b + sqrt(b*b-4*a*c), you end up with a result that has large relative error. But note that this problem only applies to one of the two roots: in -b - sqrt(b*b-4*a*c), there's no such problem. Similarly, for b large and negative, the first root is fine, but the second may be subject to loss of accuracy.

The solution is to use your existing formula to compute whichever of the roots doesn't have the cancellation problem, and then use a different formula for the other root (essentially, using the fact that you know that the product of the two roots is c / a). That formula is 2c / (-b +/- sqrt(b*b-4*a*c)).

Here's some example code. It uses math.copysign to pick the sign that won't lead to cancellation error:

>>> from math import sqrt, copysign
>>> def quadratic_roots(a, b, c):
...     discriminant = b*b - 4*a*c
...     q = -b - copysign(sqrt(discriminant), b)
...     root1 = q / (2*a)
...     root2 = (2*c) / q
...     return root1, root2
... 
>>> quadratic_roots(a=1e-8, b=10, c=1e-8)
>>> (-1000000000.0, -1e-09)

This deals with the most serious possible cause of numerical instability. There's a second possible cause, in the computation of the discriminant, if b*b happens to be very close to 4*a*c. In that case, it's possible to lose up to half the correct significant figures (so you'll get only 7-8 accurate digits for each root). Getting full-precision results in that case would require computing the discriminant using extended precision.

The wikipedia article on loss of significance contains a useful discussion of exactly this problem.


Solution 3:

You can also use the bigfloat library for the same, with arbitrary precision.

from bigfloat import sub, add, mul, div, sqr, sqrt, precision

a=1e-8
b=10
c=1e-8
p = 100

D = sub(sqr(b) , mul(4, mul(a,c) ), precision(p))

x1 = div( - add(b , sqrt(D, precision(p))) , mul(2,a), precision(p))
x2 = div( - sub(b , sqrt(D, precision(p))) , mul(2,a), precision(p))

print x1,x2

-999999999.99999997907743916987153 -9.9999999999981901320509082432747e-10

Post a Comment for "How Can I Get More Exact Decimal Values In Python"