Je me trouve dans une situation où le calcul de 1.77e-308/10
déclenche une exception de 1.777e-308/10
, mais pas celui de 1.777e-308/10
. C’est étrange parce que:
Il y a sous-dépassement lorsque le résultat réel d’une opération en virgule flottante est d’une magnitude inférieure (c’est-à-dire plus proche de zéro) que la plus petite valeur pouvant être représentée sous la forme d’un nombre à virgule flottante normal dans le type de données cible (d’après Arithmetic Underflow, Wikipedia).
En d’autres termes, si nous calculons x/y
où x
et y
sont double
, alors il devrait y avoir un dépassement négatif si 0 < |x/y| < 2.2251e-308
0 < |x/y| < 2.2251e-308
(le plus petit double
normalisé positif est 2.2251e-308
). En théorie, donc, les versions 1.77e-308/10
et 1.777e-308/10
devraient déclencher une exception de 1.777e-308/10
. La théorie est en contradiction avec ce que j’ai testé avec le programme C ci-dessous.
#include #include #include int main(){ double x,y; // x = 1.77e-308 => underflow // x = 1.777e-308 gives ==> no underflow x=1.77e-308; feclearexcept(FE_ALL_EXCEPT); y=x/10.0; if (fetestexcept(FE_UNDERFLOW)) { puts("Underflow\n"); } else puts("No underflow\n"); }
Pour comstackr le programme, j’ai utilisé gcc program.c -lm
; J’ai aussi essayé Clang, ce qui m’a donné le même résultat. Une explication?
[Modifications] J’ai partagé le code ci-dessus via cet IDE en ligne .
Le sous-débit n’est pas seulement une question de scope, mais également de précision / arrondi.
7.12.1 Traitement des conditions d’erreur
Le résultat est insuffisant si l’ampleur du résultat mathématique est si petite que le résultat mathématique ne peut pas être représenté, sans erreur d’arrondi extraordinaire, dans un object du type spécifié. C11 §7.12.1 6
1.777e-308, converti au fichier binary64 le plus proche 0x1.98e566222bcfcp-1023, se trouve avoir une signification (0x198E566222BCFC, 7193376082541820) qui est un multiple de 10. Donc, diviser par 10 est donc exact. Aucune erreur d’arrondi.
Je trouve cela plus facile à démo avec la notation hexadécimale. Notez que la division par 2 est toujours exacte, à l’exception de la plus petite valeur.
#include #include #include #include int uf_test(double x, double denominator){ printf("%.17e %24a ", x, x); feclearexcept(FE_ALL_EXCEPT); double y=x/denominator; int uf = !!fetestexcept(FE_UNDERFLOW); printf("%-24a %s\n", y, uf ? "Underflow" : ""); return uf; } int main(void) { uf_test(DBL_MIN, 2.0); uf_test(1.777e-308, 2.0); uf_test(1.77e-308, 2.0); uf_test(DBL_TRUE_MIN, 2.0); uf_test(pow(2.0, -1000), 10.0); uf_test(DBL_MIN, 10.0); uf_test(1.777e-308, 10.0); uf_test(1.77e-308, 10.0); uf_test(DBL_TRUE_MIN, 10.0); return 0; }
Sortie
2.22507385850720138e-308 0x1p-1022 0x1p-1023 1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.98e566222bcfcp-1024 1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.97490d21e478cp-1024 4.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow // No underflow as inexact result is not too small 9.33263618503218879e-302 0x1p-1000 0x1.999999999999ap-1004 // Underflow as result is too small and inexact 2.22507385850720138e-308 0x1p-1022 0x1.99999999999ap-1026 Underflow // No underflow as result is exact 1.77700000000000015e-308 0x1.98e566222bcfcp-1023 0x1.471deb4e8973p-1026 1.77000000000000003e-308 0x1.97490d21e478cp-1023 0x1.45d40a818394p-1026 Underflow 4.94065645841246544e-324 0x1p-1074 0x0p+0 Underflow
Vérifier la documentation de la fonction que vous avez appelée conduit à la définition:
FE_UNDERFLOW le résultat d’une opération antérieure en virgule flottante était inférieur à la normale avec une perte de précision
http://en.cppreference.com/w/c/numeric/fenv/FE_exceptions
Vous avez vérifié que votre nombre est inférieur à la normale, je pense. Le test inclut également une perte de précision. Si vous imprimez des chiffres plus significatifs, vous constaterez que celui qui a signalé le débordement semble perdre de la précision à environ 16 décimales. Je ne sais pas combien de chiffres significatifs il faut s’attendre à un nombre inférieur à la normale, mais je pense que cela doit être votre réponse.