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.
la source
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.Donald Knuth a une proposition pour un algorithme de comparaison à virgule flottante dans le volume 2 "Algorithmes séminariques" de "L'art de la programmation informatique". Il a été implémenté en C par Th. Belding (voir package fcmp ) et est disponible dans le GSL .
la source
abs(a-b)/max(a, b) < eps
, nous le faisonsabs(a-b)/2**exponent(max(a, b)) < eps
, ce qui laisse pratiquement tomber la mantisse dans lemax(a, b)
, donc à mon avis, la différence est négligeable.Les nombres dénormalisés de manière optimale arrondie peuvent en effet avoir une erreur relative élevée. (Rincer cela à zéro tout en l'appelant une erreur relative est trompeur.)
Mais proche de zéro, le calcul d'erreurs relatives n'a aucun sens.
Par conséquent, avant même d'atteindre des nombres dénormalisés, vous devez probablement passer à la précision absolue (à savoir celle que vous souhaitez garantir dans ce cas).
Les utilisateurs de votre code savent alors exactement combien de précision ils ont réellement.
la source
abs(a-b) < tiny(1._dp)
comme je le fais ci-dessus.tiny(1._dp)=2.22507385850720138E-308
(j'ai fait une erreur dans mon commentaire précédent, c'est 2e-308, pas 1e-320). C'est donc mon erreur absolue. Ensuite, je dois comparer l'erreur relative. Je vois votre point, je pense que vous avez raison. Merci!