J'ai une fonction numérique f(x, y)
renvoyant un double nombre à virgule flottante qui implémente une formule et je veux vérifier qu'elle est correcte par rapport aux expressions analytiques pour toutes les combinaisons de paramètres x
et y
que je suis intéressé. Quelle est la bonne façon de comparer les données calculées et nombres analytiques à virgule flottante?
Disons que les deux nombres sont a
et b
. Jusqu'à présent, je me suis assuré que les erreurs absolues ( abs(a-b) < eps
) et relatives ( abs(a-b)/max(abs(a), abs(b)) < eps
) sont inférieures à eps. De cette façon, il captera les inexactitudes numériques même si les chiffres sont disons autour de 1e-20.
Cependant, aujourd'hui, j'ai découvert un problème, la valeur numérique a
et la valeur analytique b
étaient:
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
L'erreur absolue [50] est donc (évidemment) petite, mais l'erreur relative [52] est grande. J'ai donc pensé que j'avais un bug dans mon programme. En déboguant, je me suis rendu compte que ces nombres sont dénormaux . En tant que tel, j'ai écrit la routine suivante pour faire la comparaison relative appropriée:
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
Où tiny(1._dp)
renvoie 2.22507385850720138E-308 sur mon ordinateur. Maintenant, tout fonctionne et j'obtiens simplement 0 comme erreur relative et tout va bien. En particulier, l'erreur relative ci-dessus [52] est erronée, elle est simplement causée par une précision insuffisante des nombres dénormaux. Ma mise en œuvre de la rel_error
fonction est-elle correcte? Dois-je simplement vérifier que abs(a-b)
c'est moins que minuscule (= dénormal) et retourner 0? Ou devrais-je vérifier une autre combinaison, comme
max(abs(a), abs(b))
?
Je voudrais juste savoir quelle est la "bonne" façon.
exp(log_gamma(m+0.5_dp) - (m+0.5_dp)*log(t)) / 2
pour m = 234, t = 2000. Il passe rapidement à zéro à mesure que j'augmentem
. Tout ce que je veux m'assurer, c'est que ma routine numérique renvoie des nombres "corrects" (pour retourner zéro, c'est très bien aussi) à au moins 12 chiffres significatifs. Donc, si le calcul renvoie un nombre dénormal, il est simplement nul et il ne devrait pas y avoir de problème. Donc, juste la routine de comparaison doit être robuste contre cela.