На изображении выше показана галактика из моделирования, созданного Исследовательской группой Хопкинса в Калифорнийском технологическом институте. Подобные симуляции являются важной частью современной астрономии и могут потребовать огромных вычислительных мощностей. Они также могут потребовать использования высокоточной арифметики для получения значимых результатов. Например, в одном исследовании нашей Солнечной системы, которое было рассчитано с использованием арифметики с двойной точностью, было установлено, что вычисленный угол имел ошибку более 1 радиана из-за округления!

В этой краткой статье рассматривается причина № 1, по которой в числовых расчетах теряется точность: что это такое и что с этим делать.

Давайте начнем с довольно простого примера, взятого из научной литературы (см. ссылку в конце этой статьи). Я буду использовать для этого Julia, но используемые принципы применимы к высокоточным вычислениям на любом языке. Мы хотим вычислить значение

На первый взгляд это выглядит достаточно просто. Просто запустите Julia и введите выражения:

julia> a = sin(10^22);
julia> b = log(17.1);
julia> c = exp(0.42);
julia> 173746a + 94228b - 78487c
2.9103830456733704e-11

У нас есть ответ, и Джулия не жаловалась. Проблема в том, что полученный нами ответ не очень близок к правильному значению -1,3418189578e-12! Что случилось? Давайте посмотрим на отдельные термины в нашем выражении.

julia> t1 = 173746a
julia> t2 = 94228b
julia> t3 = 78487c
julia> println("t1 = $t1, t2 = $t2, t3 = $t3")
t1 = -148066.48884364998, t2 = 267520.6854594897, t3 = 119454.1966158397
julia> println("t1+t2 = $(t1+t2)")
t1+t2 = 119454.19661583973

Обратите внимание, что t1+t2 очень близко к значению t3. Это приводит к известной проблеме численных вычислений, которая может привести к большим потерям точности. Когда вычитаются два числа, очень близкие друг к другу, общие цифры фактически сокращаются, и только оставшиеся цифры остаются значимыми. Легче всего это увидеть, если делать вычитание так, как это делали в школе, выстраивая цифры:

В этом примере при вычитании двух чисел остается только одна цифра точности, что фактически делает расчет недействительным.

Итак, что вы можете сделать по этому поводу? В некоторых случаях вы можете перестроить вычисление терминов, чтобы избежать подобных вычитаний. Однако, если это невозможно, вы обычно можете использовать высокоточную арифметику и увеличить точность значений до точки, при которой достигается достаточная точность. Вот тот же расчет, но выполненный со значениями BigFloat, чтобы получить лучший результат:

julia> abig = sin(BigFloat("1.0e22"));
julia> bbig = log(BigFloat("17.1"));
julia> cbig = exp(BigFloat("0.42"));
julia> coeffa = BigFloat("173746");
julia> coeffb = BigFloat("94228");
julia> coeffc = BigFloat("78487");
julia> coeffa*abig + coeffb*bbig - coeffc*cbig
-1.341818957829619549704278684230958880945236623213976217962437160120011379004573e-12

Ссылка «Почему и как использовать произвольную точность» Гази и др. al., Вычисления в науке и технике, Vol. 12, выпуск 3, май-июнь 2010 г.