65 votes

Une valeur flottante proche de zéro peut-elle causer une erreur de division par zéro?

Tout le monde sait que vous n'êtes pas censé comparer les flottants directement, mais plutôt utiliser une tolérance:

 float a,b;
float epsilon = 1e-6f;
bool equal = (fabs(a-b) < epsilon);
 

Je me demandais si la même chose s’appliquait à la comparaison d’une valeur à zéro avant de l’utiliser en division.

 float a, b;
if (a != 0.0f) b = 1/a; // oops?
 

Dois-je également comparer avec epsilon dans ce cas?

70voto

R.. Points 93718

Floating point de la division par zéro n'est pas une erreur. Il soulève une exception de virgule flottante (qui est un no-op, sauf si vous êtes activement à les vérifier) sur des implémentations de soutien à virgule flottante exceptions, et a bien défini résultat: positif ou négatif à l'infini (si le numérateur est différente de zéro), ou NAN (si le numérateur est égal à zéro).

Il est également possible d'obtenir de l'infini (et une exception de dépassement de capacité) comme le résultat lorsque le dénominateur est différent de zéro, mais très proche de zéro (par ex. subnormale), mais encore une fois ce n'est pas une erreur. C'est juste la façon dont virgule flottante œuvres.

Edit: Notez que, comme Eric l'a souligné dans les commentaires, cette réponse suppose exigences de l'Annexe F, une partie facultative de la norme C détaillant à virgule flottante comportement et en l'alignant avec la norme IEEE pour les calculs en virgule flottante. En l'absence de l'IEEE arithmétique, C ne définit pas de virgule flottante de division par zéro (et en fait, les résultats de toutes les opérations en virgule flottante sont définis par l'implémentation et peut être défini comme un non-sens complet et à se conformer à la norme C), donc si vous avez affaire à un farfelu C mise en œuvre qui ne fait pas honneur à virgule flottante IEEE, vous devrez consulter la documentation de l'application que vous utilisez pour répondre à cette question.

26voto

Eric Postpischil Points 36641

Oui, en divisant par petits nombres peuvent provoquer les mêmes effets que la division par zéro, y compris les pièges, dans certaines situations.

Certaines implémentations C (et quelques autres environnements informatiques) peut exécuter dans une chasse d'eau-de type dépassement de mode, en particulier si les options de haute performance sont utilisés. Dans ce mode, en divisant par un des nombres dénormalisés peut provoquer le même résultat que la division par zéro. Flush-underflow mode n'est pas rare quand le vecteur (SIMD) instructions sont utilisées.

Nombres dénormalisés numéros sont ceux avec le moins d'exposant dans le floating point format qui sont si petits que l'implicite peu de la significande est 0 au lieu de 1. Pour la norme IEEE 754, simple précision, c'est non nulle numéros avec une amplitude de moins de 2-126. Pour la double précision, il est non-zéro numéros avec une amplitude de moins de 2-1022.

La manipulation des nombres dénormalisés correctement les numéros (en conformité avec la norme IEEE 754) nécessite plus de temps de calcul dans certains processeurs. Pour éviter ce délai lorsqu'il n'est pas nécessaire, les transformateurs peuvent avoir un mode de convertir des nombres dénormalisés opérandes à zéro. La division d'un nombre par un des nombres dénormalisés opérande produira le même résultat que la division par zéro, même si le résultat habituel serait finie.

Comme indiqué dans d'autres réponses, la division par zéro n'est pas une erreur en C des implémentations d'adopter l'Annexe F de la norme. Pas toutes les implémentations qui ne. Dans les implémentations qui ne le font pas, vous ne pouvez pas être sûr de savoir si à virgule flottante pièges sont activés, en particulier le piège de la division par zéro exception, sans autres spécifications relatives à votre environnement.

Selon votre situation, vous pourriez également avoir à se prémunir contre un autre code dans votre application modifiant la virgule flottante de l'environnement.

9voto

dasblinkenlight Points 264350

Pour répondre à la question figurant dans le titre de votre message, diviser par un très petit nombre n'entraînera pas de division par zéro, mais le résultat pourrait devenir un infini:

 double x = 1E-300;
cout << x << endl;
double y = 1E300;
cout << y << endl;
double z = y / x;
cout << z << endl;
cout << (z == std::numeric_limits<double>::infinity()) << endl;
 

Cela produit la sortie suivante:

 1e-300
1e+300
inf
1
 

8voto

Jens Agby Points 367

Seule une division d’exactement 0.f déclenche une exception division par zéro.

Cependant, la division par un très petit nombre peut générer une exception de dépassement de capacité - le résultat est si grand qu'elle ne peut plus être représentée par un float. La division retournera l'infini.

La représentation float de l'infini peut être utilisée dans les calculs, il n'est donc peut-être pas nécessaire de la vérifier si le reste de votre implémentation est capable de la gérer.

3voto

Reed Copsey Points 315315

Dois-je aussi besoin de comparer avec epsilon dans ce cas?

Vous n'aurez plus jamais recevoir une erreur de division par zéro, 0.0f est représenté exactement dans un IEEE flotteur.

Cela étant dit, vous pouvez toujours utiliser une certaine tolérance - bien que cela dépend entièrement de votre application. Si la valeur "zéro" est le résultat d'autres mathématiques, il est possible d'obtenir de très petite taille, non-nulle, ce qui peut causer un inattendu résultat après votre division. Si vous voulez traiter "proche de zéro" numéros zéro, une tolérance serait approprié. Cela dépend entièrement de votre demande et vos objectifs, cependant.

Si votre compilateur est à l'aide de la norme IEEE 754 normes pour la gestion des exceptions, puis diviser par zéro, ainsi qu'une division par une valeur qui est assez petit pour provoquer un dépassement de capacité, permettrait à la fois de produire une valeur de +/- infiniti. Cela pourrait signifier que vous pourriez vouloir inclure la vérification de très petits nombres (qui serait la cause d'un dépassement de capacité sur votre plate-forme). Par exemple, sur Windows, float et double à la fois conforme à la norme, ce qui pourrait provoquer un très petit diviseur de créer de +/- infiniti, tout comme une valeur de zéro.

Si votre compilateur/plate-forme n'est pas suivant la norme IEEE 754 virgule flottante normes, alors je crois que les résultats sont spécifiques de la plateforme.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X