Если вы ищете надежную границу для ошибки округления, вам не обязательно нужна библиотека с точностью до aribtrary. Вместо этого вы можете использовать анализ ошибок.
Мне не удалось найти хорошую онлайн-справку, но все это описано в разделе 3.3 книги Ника Хайама «Точность и стабильность численных алгоритмов». Идея довольно проста:
- Перефакторинг вашего кода, чтобы у вас было одно назначение одной арифметической операции в каждой строке.
- Например, для каждой переменной
x
создайте переменную, x_err
которая инициализируется нулем, когда ей x
присваивается константа.
- Например, для каждой операции
z = x * y
обновите переменную, z_err
используя стандартную модель арифметики с плавающей запятой, а также возникающие z
и текущие ошибки x_err
и y_err
.
- Возвращаемое значение вашей функции должно также иметь соответствующее
_err
значение. Это зависит от данных на вашей общей ошибки округления.
Сложная часть - шаг 3. Для самых простых арифметических операций вы можете использовать следующие правила:
z = x + y
-> z_err = u*abs(z) + x_err + y_err
z = x - y
-> z_err = u*abs(z) + x_err + y_err
z = x * y
-> z_err = u*abs(z) + x_err*abs(y) + y_err*abs(x)
z = x / y
-> z_err = u*abs(z) + (x_err*abs(y) + y_err*abs(x))/y^2
z = sqrt(x)
-> z_err = u*abs(z) + x_err/(2*abs(z))
где u = eps/2
блок округления. Да, правила для +
и -
одинаковы. Правила для любой другой операции op(x)
могут быть легко извлечены с использованием разложения в ряд Тейлора для примененного результата op(x + x_err)
. Или вы можете попробовать поискать в Google. Или используя книгу Ника Хайама.
В качестве примера рассмотрим следующий код Matlab / Octave, который оценивает полиномы в коэффициентах a
в точке, x
используя схему Хорнера:
function s = horner ( a , x )
s = a(end);
for k=length(a)-1:-1:1
s = a(k) + x*s;
end
Для первого шага мы разделили две операции на s = a(k) + x*s
:
function s = horner ( a , x )
s = a(end);
for k=length(a)-1:-1:1
z = x*s;
s = a(k) + z;
end
Затем мы вводим _err
переменные. Обратите внимание, что входные данные a
и x
предполагаются точными, но мы также можем также потребовать от пользователя передать соответствующие значения для a_err
и x_err
:
function [ s , s_err ] = horner ( a , x )
s = a(end);
s_err = 0;
for k=length(a)-1:-1:1
z = x*s;
z_err = ...;
s = a(k) + z;
s_err = ...;
end
Наконец, мы применяем правила, описанные выше, чтобы получить условия ошибки:
function [ s , s_err ] = horner ( a , x )
u = eps/2;
s = a(end);
s_err = 0;
for k=length(a)-1:-1:1
z = x*s;
z_err = u*abs(z) + s_err*abs(x);
s = a(k) + z;
s_err = u*abs(s) + z_err;
end
Обратите внимание, что поскольку у нас нет a_err
или x_err
, например, они предполагаются равными нулю, соответствующие выражения просто игнорируются в выражениях ошибок.
И вуаля! Теперь у нас есть схема Хорнера, которая возвращает оценку ошибки в зависимости от данных (примечание: это верхняя граница ошибки) вместе с результатом.
В качестве примечания, поскольку вы используете C ++, вы можете рассмотреть возможность создания собственного класса для значений с плавающей запятой, который переносит _err
термин и перегружает все арифметические операции для обновления этих значений, как описано выше. Для больших кодов это может быть более простой, хотя и менее эффективный в вычислительном отношении маршрут. Сказав это, вы можете найти такой класс в Интернете. Быстрый поиск в Google дал мне эту ссылку .
PS Обратите внимание, что все это работает только на машинах, строго придерживающихся IEEE-754, то есть все арифметические операции с точностью до . Этот анализ также дает более жесткую, более реалистичную границу, чем использование интервальной арифметики, поскольку по определению вы не можете представлять число в плавающей точке, т.е. ваш интервал будет просто округляться до самого числа.x ( 1 ± u )± тых ( 1 ± у )