Вам не нужны дополнительная точность, чтобы решить эту проблему: 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, содержится полезное обсуждение именно этой проблемы.