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 xet yque 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 aet 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 aet 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_errorfonction 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)) / 2pour 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.