56 votes

Le comportement de la division par zéro en virgule flottante

Pensez à

#include <iostream>
int main()
{
    double a = 1.0 / 0;
    double b = -1.0 / 0;
    double c = 0.0 / 0;
    std::cout << a << b << c; // to stop compilers from optimising out the code.    
}

J'ai toujours pensé que a sera +Inf, b sera -Inf, et c sera NaN. Mais j'ai également entendu des rumeurs selon lesquelles le comportement de la division par zéro en virgule flottante est, à strictement parler, le suivant indéfini et donc le code ci-dessus ne peut pas être considéré comme du C++ portable. (Cela oblitère théoriquement l'intégrité de ma pile de code de plus d'un million de lignes. Oups).

Qui a raison ?

Je suis satisfait de la note mise en œuvre définie mais je parle de manger des chats, d'aspirer des démons comportement indéfini ici.

1 votes

Umm : "Voulez-vous dire std::cout << a << b << c; " ?

1 votes

@WhiZTiM : naturellement ;-)

2 votes

@WhiZTiM Je l'ai vu. Nous l'avons tous vu. Je suis désolé mais vous n'êtes pas un grand Google ;)

42voto

Adrian Maire Points 6604

La norme C++ n'impose pas la norme IEEE 754, car celle-ci dépend essentiellement de l'architecture matérielle.

Si le matériel/compilateur implémente correctement le standard IEEE 754, la division fournira les INF, -INF et NaN attendus, sinon... ça dépend.

Undefined signifie que c'est l'implémentation du compilateur qui décide, et il y a de nombreuses variables à cela comme l'architecture matérielle, l'efficacité de la génération de code, la paresse du développeur du compilateur, etc.

Source :

La norme C++ stipule qu'une division par 0.0 est undefined

Norme C++ 5.6.4

... Si le deuxième opérande de / ou % est zéro, le comportement est indéfini.

Norme C++ 18.3.2.4

...statique constexpr bool is_iec559 ;

...56. Vrai si et seulement si le type adhère à la norme IEC 559.217

...57. Significatif pour tous les types de virgule flottante.

Détection C++ de IEEE754 :

La bibliothèque standard inclut un modèle pour détecter si IEEE754 est supporté ou non :

statique constexpr bool is_iec559 ;

#include <numeric>
bool isFloatIeee754 = std::numeric_limits<float>::is_iec559();

Que faire si IEEE754 n'est pas supporté ?

Cela dépend, généralement une division par 0 déclenche une exception matérielle et provoque l'arrêt de l'application.

4 votes

Je ne suis pas sûr de vos affirmations dans le deuxième paragraphe. UB est UB, et de bons compilateurs pourrait choisir de ne pas compiler le code .

0 votes

@KerrekSB : C'est mieux maintenant ? ou pourriez-vous expliquer davantage votre préoccupation s'il vous plaît ?

1 votes

Eh bien, UB est UB, et il est parfaitement plausible pour un compilateur de supposer que vous allez pas causer UB, et de sorte que la division ne peut être atteint ... Donc ça peut être délicat. Je ne suis pas sûr que je serais à l'aise en supposant que que j'obtienne le comportement de la division IEEE-754.

25voto

Quentin Points 3904

Citation : Référence cpp :

Si le second opérande est zéro, le comportement est indéfini, sauf si une division en virgule flottante a lieu et que le type supporte l'arithmétique en virgule flottante IEEE (cf. std::numeric_limits::is_iec559 ), alors :

  • si un opérande est NaN, le résultat est NaN

  • diviser un nombre non nul par ±0,0 donne l'infini correctement signé et FE_DIVBYZERO est soulevé

  • diviser 0.0 par 0.0 donne NaN et FE_INVALID est soulevé

Nous parlons ici d'une division en virgule flottante, il est donc nécessaire de définir l'implémentation de la division. double la division par zéro est indéfinie.

Si std::numeric_limits<double>::is_iec559 est true et c'est "habituellement true " alors le comportement est bien défini et produit les résultats attendus.

Un pari plutôt sûr serait de se poser un.. :

static_assert(std::numeric_limits<double>::is_iec559, "Please use IEEE754, you weirdo");

... près de votre code.

3 votes

Je ne vois rien dans la norme C++ qui justifie cette clause "except". Est-ce que cppreference suppose que c'est parce que c'est ce que dit IEC669 ?

0 votes

Je suis d'accord avec @Martin ici ; 5.6/4 est très clair sur le fait que le zéro sur l'EDS d'un / est indéfini. Mais il y a aussi 18.3.2.4/56 "True if and only if the type adheres to IEC 559 standard" (vrai si et seulement si le type adhère à la norme IEC 559) qui pourrait être interprété comme une contradiction (personnellement, je ne pense pas que cela signifie que toutes les opérations arithmétiques doivent fonctionner comme le voudrait la CEI 559, mais bon). Cependant, il n'y a rien qui puisse justifier la certitude affichée par cppreference ici.

3 votes

Veuillez citer des documents normalisés, pas des sites Web aléatoires ;)

13voto

Shafik Yaghmour Points 42198

La division par zéro, à la fois en nombres entiers et en virgule flottante, est un comportement non défini. [expr.mul]p4 :

L'opérateur binaire / donne le quotient, et l'opérateur binaire % donne le reste de la division de la première expression par la seconde. de la première expression par la seconde. Si le deuxième opérande de / ou % est zéro, le comportement est indéfini. ...

Bien que la mise en œuvre puisse optionnellement prendre en charge Annexe F qui possède une sémantique bien définie pour la division par zéro en virgule flottante.

Nous pouvons voir dans ce rapport de bogue de Clang clang sanitizer considère la division par zéro en virgule flottante de la CEI 60559 comme non définie. que même si la macro __STDC_IEC_559__ est définie, elle est définie par les en-têtes du système et, au moins pour clang, elle ne supporte pas Annexe F et donc pour clang reste un comportement non défini :

L'annexe F de la norme C (support IEC 60559 / IEEE 754) définit la division par zéro en virgule flottante, mais clang (3.3 et 3.4 Debian snapshot) la considère comme indéfinie. Ceci est incorrect :

Le soutien de l'annexe F est facultatif, et nous ne le soutenons pas.

if STDC_IEC_559

Cette macro est définie par les en-têtes de votre système, pas par nous. un bogue dans les en-têtes de votre système. (FWIW, GCC ne supporte pas complètement Annex F non plus, IIRC, donc ce n'est même pas un bug spécifique à Clang).

Ce rapport de bogue et deux autres rapports de bogue UBSan : La division par zéro en virgule flottante n'est pas indéfinie et clang devrait supporter l'annexe F de l'ISO C (IEC 60559 / IEEE 754) indique que gcc est conforme à Annexe F par rapport à la division par zéro en virgule flottante.

Bien que je sois d'accord que ce n'est pas à la bibliothèque C de définir STDC_IEC_559 sans condition, le problème est spécifique à clang. GCC ne supporte pas complètement l'annexe F, mais au moins son intention est de la supporter par défaut et la division est bien définie avec elle si le mode d'arrondi n'est pas changé. De nos jours, ne pas supporter l'IEEE 754 (au moins les fonctionnalités de base comme la gestion de la division par zéro) est considéré comme un mauvais comportement.

Ceci est soutenu par la gcc Sémantique des mathématiques à virgule flottante dans le wiki GCC ce qui indique que -sans-signal-nans est la valeur par défaut qui est en accord avec la optimisations gcc options documentation qui dit :

La valeur par défaut est -fno-signaling-nans.

Il est intéressant de noter que UBSan pour clang, la valeur par défaut est d'inclure float-divide-by-zero sous -fsanitize=undefined tandis que gcc n'est pas :

Détecter la division par zéro en virgule flottante. Contrairement à d'autres options similaires, -fsanitize=float-divide-by-zero n'est pas activé par -fsanitize=undefined, car la division par zéro en virgule flottante peut être un moyen légitime d'obtenir des infinis et des NaN.

Voir vivre pour clang et vivre pour gcc .

8voto

dbush Points 8590

La division par 0 est comportement indéfini .

Extrait de la section 5.6 de la Norme C++ (C++11) :

Le binaire / donne le quotient, et l'opérateur binaire % opérateur donne le reste de la division de la première expression par la seconde. seconde. Si le deuxième opérande de / ou % est égal à zéro, le comportement est indéfini. Pour les opérandes intégraux, le / donne le quotient algébrique avec toute partie fractionnaire éliminée ; si le quotient a/b est représentable dans le type du résultat, (a/b)*b + a%b est égal à a .

Aucune distinction n'est faite entre les opérandes entiers et les opérandes à virgule flottante pour la fonction / opérateur. La norme indique seulement que la division par zéro est indéfinie sans tenir compte des opérandes.

3 votes

Notez que dans mon cas, il ne s'agit pas d'une division intégrale par zéro. La raison pour laquelle je doute de la validité est que % ne s'applique pas aux types non intégraux en C++. Le C++ n'est pas Java, vous savez.

1 votes

@Bathsheba Le passage cité ne dit rien sur les opérandes entiers ou à virgule flottante concernant la division par zéro, juste que c'est indéfini. En fait, l'un des commentaires sur la question donne un exemple d'une erreur de compilation dans ce cas.

0 votes

Le paragraphe qui précède la citation fait une "exception" pour les personnes suivantes % : Les opérandes de * et / doivent être de type arithmétique ou d'énumération non spécifiée ; les opérandes de % doivent être de type intégral ou d'énumération non spécifiée.

6voto

NathanOliver Points 10062

Dans [expr]/4 nous avons

Si pendant l'évaluation d'une expression, le résultat n'est pas défini mathématiquement ou pas dans l'intervalle des valeurs représentables pour son type, le comportement est indéfini . [Note : la plupart des implémentations existantes du C++ ignorent les débordements d'entiers. Le traitement de la division par zéro, de la formation d'un reste à l'aide d'un diviseur nul et de toutes les exceptions en virgule flottante varie selon les machines et est généralement réglable par une fonction de bibliothèque. -fin de la note ]

C'est moi qui souligne

Selon la norme, il s'agit donc d'un comportement non défini. Elle poursuit en disant que certains de ces cas sont en fait gérés par l'implémentation et sont configurables. Elle ne dit donc pas qu'il s'agit d'un comportement défini par l'implémentation, mais elle vous fait savoir que les implémentations définissent certains de ces comportements.

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