У меня есть числовая функция, 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 значащих цифр. Так что, если вычисление возвращает ненормальное число, тогда оно просто равно нулю, и проблем не должно быть. Так что только процедура сравнения должна быть устойчивой к этому.