У меня есть числовая функция, f(x, y)
возвращающая двойное число с плавающей запятой, которая реализует некоторую формулу, и я хочу проверить, является ли она корректной по отношению к аналитическим выражениям для всех комбинаций параметров x
и y
которая мне интересна. Как правильно сравнивать вычисленные и аналитические числа с плавающей точкой?
Допустим, два числа a
и b
. До сих пор я следил за тем, чтобы как абсолютные ( abs(a-b) < eps
), так и относительные ( abs(a-b)/max(abs(a), abs(b)) < eps
) ошибки были меньше, чем eps. Таким образом, он улавливает неточности чисел, даже если числа, скажем, около 1e-20.
Однако сегодня я обнаружил проблему, числовое значение a
и аналитическое значение b
были:
In [47]: a
Out[47]: 5.9781943146790832e-322
In [48]: b
Out[48]: 6.0276008792632078e-322
In [50]: abs(a-b)
Out[50]: 4.9406564584124654e-324
In [52]: abs(a-b) / max(a, b)
Out[52]: 0.0081967213114754103
Таким образом, абсолютная ошибка [50] (очевидно) мала, но относительная ошибка [52] велика. Поэтому я подумал, что у меня есть ошибка в моей программе. Отладив, я понял, что эти числа ненормальны . Таким образом, я написал следующую процедуру для правильного сравнения:
real(dp) elemental function rel_error(a, b) result(r)
real(dp), intent(in) :: a, b
real(dp) :: m, d
d = abs(a-b)
m = max(abs(a), abs(b))
if (d < tiny(1._dp)) then
r = 0
else
r = d / m
end if
end function
Где tiny(1._dp)
возвращает 2.22507385850720138E-308 на моем компьютере. Теперь все работает, и я просто получаю 0 как относительную ошибку, и все в порядке. В частности, приведенная выше относительная ошибка [52] неверна, просто она вызвана недостаточной точностью денормальных чисел. Правильно ли реализована моя rel_error
функция? Должен ли я просто проверить, что abs(a-b)
он меньше, чем крошечный (= денормальный), и вернуть 0? Или я должен проверить какую-то другую комбинацию, как
max(abs(a), abs(b))
?
Я просто хотел бы знать, что такое «правильный» способ.
источник
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
к m = 234, t = 2000. По мере увеличения он быстро уходит в нольm
. Все, что я хочу убедиться, что моя числовая подпрограмма возвращает «правильные» числа (для нуля тоже отлично) по меньшей мере до 12 значащих цифр. Так что, если вычисление возвращает ненормальное число, тогда оно просто равно нулю, и проблем не должно быть. Так что только процедура сравнения должна быть устойчивой к этому.У Дональда Кнута есть предложение для алгоритма сравнения с плавающей запятой в томе 2 «Получисленные алгоритмы» «Искусства компьютерного программирования». Это было реализовано в C на Th. Belding (см. Пакет fcmp ) и доступен в GSL .
источник
abs(a-b)/max(a, b) < eps
, мы делаемabs(a-b)/2**exponent(max(a, b)) < eps
, что в значительной степени просто отбрасывает мантиссуmax(a, b)
, поэтому, на мой взгляд, разница незначительна.Оптимально округленные денормализованные числа действительно могут иметь высокую относительную ошибку. (Сброс этого до нуля, в то же время называя его относительной ошибкой, вводит в заблуждение.)
Но, приближаясь к нулю, вычислять относительные ошибки бессмысленно.
Следовательно, даже до того, как вы достигнете денормализованных чисел, вам, вероятно, следует переключиться на абсолютную точность (а именно ту, которую вы хотите гарантировать в этом случае).
Тогда пользователи вашего кода точно знают, насколько они действительно точны.
источник
abs(a-b) < tiny(1._dp)
как я делаю выше.tiny(1._dp)=2.22507385850720138E-308
(я допустил ошибку в моем предыдущем комментарии, это 2e-308, а не 1e-320). Так что это моя абсолютная ошибка. Тогда мне нужно сравнить относительную ошибку. Я понимаю вашу точку зрения, я думаю, что вы правы. Спасибо!