2015-09-16 5 views
3
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) 

print (4*a*c) 
print (sqrt(b**2-4*a*c)) 
print b**2 
print 2*a 

При запуске программы, это возвращает:Как я могу получить более точные значения десятичных в Python

x1 = -1e+09 
x2 = 0.0 

4e-16 
10.0 
100.0 
2e-08 

Что мне нужно для x2 равным -1e-9.

Проблема, кажется, с

sqrt((b**2)-(4*a*c)) 

, поскольку это дает 10 как результат, очевидно, потому, что 4 * (10^-8) * (10^-8) практически равна 0, и считается 0 питоном.

Это приводит к:

sqrt((b**2)-(4*a*c)) = sqrt(b**2) = sqrt(10**2) = 10 

Любая помощь будет высоко оценена

ответ

6

Используйте десятичную модуль:

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) 

результаты в

x1 = -999999999.999999999000000000 
x2 = -1.0000000000E-9 
0

Вы также можете использоватьbigfloat библиотека для того же, с произвольной точностью.

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 
5

Вам не нужны дополнительная точность, чтобы решить эту проблему: Python float s уже есть достаточно точно для работы. Вам просто нужен (немного) умный алгоритм.

Ваша проблема связана с вычитанием двух почти равных расчетных значений: для b положительной и большой (по сравнению с a и c), когда вы делаете -b + sqrt(b*b-4*a*c), вы в конечном итоге с результатом, который имеет большую относительную ошибку. Но учтите, что эта проблема применима только к одному из двух корней: в -b - sqrt(b*b-4*a*c) такой проблемы нет. Аналогично, для b больших и отрицательных, первый корень прекрасен, а второй может быть утрачен с точностью.

Решение состоит в том, чтобы использовать существующую формулу для вычисления того, что из корней не имеет проблемы с отменой, а затем использовать другую формулу для другого корня (по существу, используя тот факт, что вы знаете, что продукт два корня - c/a). Эта формула: 2c/(-b +/- sqrt(b*b-4*a*c)).

Вот пример кода. Он использует math.copysign, чтобы выбрать знак, который не приведет к ошибке отмены:

>>> 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) 

Это имеет дело с самым серьезным из возможных причин численной неустойчивости. При вычислении дискриминанта есть вторая возможная причина, если b*b оказывается очень близким к 4*a*c. В этом случае можно потерять до половины правильных значимых цифр (так что вы получите только 7-8 точных цифр для каждого корня). Получение полноточных результатов в этом случае будет требует вычисления дискриминанта с использованием расширенной точности.

В статье по Википедии, посвященной loss of significance, содержится полезное обсуждение именно этой проблемы.

Смежные вопросы