web-dev-qa-db-fra.com

Avertissement C ++: division du double par zéro

Cas 1:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0.0)<<std::endl;
}

Il compile sans aucun avertissement et imprime inf. OK, C++ peut gérer la division par zéro, ( le voir en direct ).

Mais,

Cas 2:

#include <iostream>

int main()
{
    double d = 15.50;
    std::cout<<(d/0)<<std::endl;
}

Le compilateur donne l'avertissement suivant ( le voir en direct ):

warning: division by zero [-Wdiv-by-zero]
     std::cout<<(d/0)<<std::endl;

Pourquoi le compilateur donne-t-il un avertissement dans le deuxième cas?

Est-ce que 0 != 0.0?

Modifier:

#include <iostream>

int main()
{
    if(0 == 0.0)
        std::cout<<"Same"<<std::endl;
    else
        std::cout<<"Not same"<<std::endl;
}

sortie:

Same
97
Jayesh

La division en virgule flottante par zéro est bien définie par IEEE et donne l'infini (positif ou négatif en fonction de la valeur du numérateur) (ou NaN pour ± 0) ).

Pour les entiers, il n'y a aucun moyen de représenter l'infini et le langage définit l'opération à avoir comportement indéfini afin que le compilateur tente utilement de vous guider à l'écart de ce chemin.

Cependant, dans ce cas, puisque le numérateur est un double, le diviseur (0) doit également être promu en double et il n’ya aucune raison de donner un avertissement ici sans donner d'avertissement pour 0.0 donc je pense que ceci est un bug du compilateur.

107
Motti

En C++ standard, les deux cas sont comportement indéfini . Tout peut arriver, y compris le formatage de votre disque dur. Vous ne devez pas vous attendre ou compter sur "return inf. Ok" ou tout autre comportement.

Le compilateur décide apparemment de donner un avertissement dans un cas et non dans l'autre, mais cela ne signifie pas qu'un code est correct et qu'un autre ne l'est pas. C'est juste une bizarrerie de la génération d'avertissements du compilateur.

Du standard C++ 17 [expr.mul]/4:

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

42
M.M

Ma meilleure hypothèse pour répondre à cette question particulière serait que le compilateur émette un avertissement avant effectuant la conversion de int à double.

Ainsi, les étapes seraient comme ceci:

  1. Expression parse
  2. Opérateur arithmétique/(T, T2), où T=double, T2=int.
  3. Vérifiez que std::is_integral<T2>::value est true et b == 0 - cela déclenche un avertissement.
  4. Émettre un avertissement
  5. Effectuer une conversion implicite de T2 en double
  6. Effectuer une division bien définie (depuis que le compilateur a décidé d'utiliser IEEE 754).

Ceci est bien sûr une spéculation basée sur des spécifications définies par le compilateur. Du point de vue standard, nous traitons avec les comportements non définis possibles.


Notez que ceci est le comportement attendu selon documentation GCC
(en fait, il semble que ce drapeau ne puisse pas être utilisé explicitement dans GCC 8.1)

-Division par zéro
Avertir à propos de la division entière au moment de la compilation par zéro. C'est par défaut. Pour inhiber les messages d’avertissement, utilisez -Wno-div-by-zero. La division par virgule flottante par zéro n’est pas mise en garde, car elle peut constituer un moyen légitime d’obtenir des infinis et des NaN.

12
Yksisarvinen

Je ne vais pas entrer dans la débâcle UB/not UB dans cette réponse.

Je veux juste souligner que 0 et 0.0sont différents malgré 0 == 0.0 en cours d'évaluation. 0 est un int littéral et 0.0 est un double littéral.

Cependant, dans ce cas, le résultat final est le même: d/0 est une division en virgule flottante car d est double et ainsi 0 est implicitement converti en double.

9
bolov

Je dirais que foo/0 et foo/0.0 ne sont pas les mêmes. À savoir, l'effet résultant de la première (division entière ou division à virgule flottante) dépend fortement du type de foo, alors qu'il n'en est pas de même pour la seconde (il s'agira toujours d'une division à virgule flottante).

Que l'un des deux soit ou non UB est sans importance. Citant la norme:

Le comportement non défini autorisé va d'ignorer complètement la situation avec des résultats imprévisibles et de se comporter pendant la traduction ou l'exécution du programme de manière documentée et caractéristique de l'environnement (avec ou sans émission d'un message de diagnostic) , pour mettre fin à une traduction ou à une exécution (avec émission d'un message de diagnostic).

(Soulignez les mines)

Considérez l’avertissement " suggérez les parenthèses autour de l’affectation utilisée comme valeur de vérité ": le moyen de dire au compilateur que vous voulez vraiment utiliser le résultat d'une affectation est en étant explicite et en ajoutant des parenthèses autour de l'affectation. L'instruction résultante a le même effet, mais elle indique au compilateur que vous savez ce que vous faites. La même chose peut être dite à propos de foo/0.0: puisque vous dites explicitement au compilateur "Ceci est une division en virgule flottante" en utilisant 0.0 au lieu de 0, le compilateur vous fait confiance et ne le publie pas. un avertissement.

7
Cássio Renan

Cela ressemble à un bogue gcc, la documentation pour -Wno-div-by-zerodit clairement :

Ne prévenez pas la division des nombres entiers au moment de la compilation par zéro. La division en virgule flottante par zéro n'est pas prévenue, , car il peut s'agir d'un moyen légitime d'obtenir des infinis et des NaN.

et après Conversions arithmétiques usuelles couvertes en [expr.arith.conv] les deux opérandes seront double :

De nombreux opérateurs binaires qui attendent des opérandes de type arithmétique ou énumération provoquent des conversions et génèrent des types de résultat de manière similaire. Le but est de générer un type commun, qui est également le type du résultat. Ce modèle s'appelle les conversions arithmétiques habituelles, définies comme suit:

...

- Sinon, si l'un des opérandes est double, l'autre est converti en double.

et [expr.mul] :

Les opérandes de * et/doivent avoir un type d’énumération arithmétique ou sans encombrement; les opérandes de% doivent avoir un type d'énumération intégral ou non délimité. Les conversions arithmétiques habituelles sont effectuées sur les opérandes et déterminent le type du résultat.

En ce qui concerne la question de savoir si la division par virgule flottante par zéro est un comportement indéfini et en quoi une implémentation différente le semble (ma réponse ici . TL; DR; On dirait que gcc est conforme à Annexe F en ce qui concerne la division en virgule flottante par zéro, donc indéfini ne joue pas un rôle ici. La réponse serait différente pour Clang.

4
Shafik Yaghmour

La division par virgule flottante par zéro se comporte différemment de la division par un entier entier par zéro.

Le standard virgule flottante IEEE fait la distinction entre + inf et -inf, alors que les entiers ne peuvent pas stocker l'infini. La division entière par zéro résultat est un comportement indéfini. La division par virgule flottante par zéro est définie par le standard de la virgule flottante et donne + inf ou -inf.

2
Rizwan