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, ( 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 ( voyez-le 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 second cas?
Est-ce 0 != 0.0
?
Éditer:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
production:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
la source
la source
Réponses:
La division en virgule flottante par zéro est bien définie par IEEE et donne l'infini (positive ou négative selon 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 comme ayant un comportement indéfini afin que le compilateur essaie utilement de vous éloigner de ce chemin.
Cependant dans ce cas, puisque le numérateur est a
double
, le diviseur (0
) devrait également être promu au double et il n'y a aucune raison de donner un avertissement ici sans donner d'avertissement pour0.0
donc je pense que c'est un bogue du compilateur.la source
d/0
,0
est converti en type ded
.En C ++ standard, les deux cas sont des comportements indéfinis . 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 pas dans l'autre, mais cela ne signifie pas qu'un code est OK et l'autre ne l'est pas. C'est juste une bizarrerie de la génération d'avertissements du compilateur.
À partir de la norme C ++ 17 [expr.mul] / 4:
la source
std::numeric_limits<T>::is_iec559
esttrue
, puis division par zéro pourT
ne UB (et sur la plupart des plates - formes c'esttrue
pourdouble
etfloat
, bien que pour être portable , vous aurez besoin de vérifier explicitement cela avec unif
ouif constexpr
).is_iec559
commetrue
signifie que l' implémentation documente un comportement que la norme laisse indéfini. C'est juste que c'est un cas où la documentation de l'implémentation peut être lue par programme. Pas même le seul: il en va de mêmeis_modulo
pour les types entiers signés.Ma meilleure supposition pour répondre à cette question particulière serait que le compilateur émette un avertissement avant d' effectuer la conversion de
int
endouble
.Ainsi, les étapes seraient comme ceci:
/(T, T2)
, oùT=double
,T2=int
.std::is_integral<T2>::value
c'esttrue
etb == 0
- cela déclenche un avertissement.T2
endouble
Ceci est bien sûr une spéculation et est basé sur des spécifications définies par le compilateur. Du point de vue standard, nous traitons d'éventuels comportements indéfinis.
Veuillez noter que c'est un comportement attendu selon la documentation de GCC
(btw. Il semble que cet indicateur ne puisse pas être utilisé explicitement dans GCC 8.1)
la source
/
pour savoir qu'il s'agit d'une division. Si le côté gauche avait été unFoo
objet et qu'il y aurait eu unoperator/(Foo, int)
, alors ce ne serait même pas une division. Le compilateur ne connaît sa division que lorsqu'il a choisi enbuilt-in / (double, double)
utilisant une conversion implicite du côté droit. Mais cela signifie qu'il ne fait PAS une division parint(0)
, mais une division pardouble(0)
.operator /(double, int)
est certainement acceptable. Ensuite, il dit que la conversion est effectuée avant toute autre action, mais GCC pourrait vérifier rapidement siT2
est de type entier etb == 0
et émettre un avertissement si c'est le cas. Je ne sais pas si c'est entièrement conforme à la norme, mais les compilateurs ont toute liberté pour définir les avertissements et quand ils doivent être déclenchés.operator/(double,int)
existe réellement. Le compilateur peut par exemple décider d'optimiser laa/b
constanteb
en le remplaçant para * (1/b)
. Bien sûr, cela signifie que vous n'appelez plusoperator/(double,double)
au moment de l'exécution, mais le plus rapidementoperator*(double,double)
. Mais c'est maintenant l'optimiseur qui1/0
operator*
Je n'entrerai pas dans la débâcle UB / non UB dans cette réponse.
Je veux juste souligner cela
0
et0.0
sont différents malgré l'0 == 0.0
évaluation vraie.0
est unint
littéral et0.0
est undouble
littéral.Cependant, dans ce cas, le résultat final est le même:
d/0
est une division en virgule flottante car elled
est double et0
est donc implicitement convertie en double.la source
double
par unint
signifie que leint
est converti endouble
, et il est spécifié dans la norme qui se0
convertit en0.0
(conv.fpint / 2)0
est le même que0.0
0 != 0.0
?". OP ne demande jamais s'ils sont «les mêmes». De plus, il me semble que l'intention de la question est de savoir sid/0
peut se comporter différemment ded/0.0
Je dirais que
foo/0
et nefoo/0.0
sont pas les mêmes. À savoir, l'effet résultant du premier (division entière ou division en virgule flottante) dépend fortement du type defoo
, alors qu'il n'en est pas de même pour le second (ce sera toujours une division en virgule flottante).Que l'un des deux soit UB n'est pas pertinent. Citant la norme:
(Je souligne le mien)
Considérez l' avertissement « suggérer des parenthèses autour de l'affectation utilisée comme valeur de vérité »: La façon de dire au compilateur que vous voulez vraiment utiliser le résultat d'une affectation est d'être explicite et d'ajouter 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 à la0.0
place de0
, le compilateur vous fait confiance et n'émettra pas d'avertissement.la source
foo
. C'est intentionnel. Votre assertion n'est vraie que dans le cas où ilfoo
s'agit d'un type à virgule flottante.Cela ressemble à un bogue gcc, la documentation de
-Wno-div-by-zero
dit clairement :et après les conversions arithmétiques habituelles couvertes dans [expr.arith.conv] les deux opérandes seront doubles :
et [expr.mul] :
En ce qui concerne la question de savoir si la division en virgule flottante par zéro est un comportement indéfini et la manière dont une implémentation différente la traite, je pense que ma réponse ici . TL, DR; Il semble que gcc soit conforme à l' Annexe F en ce qui concerne la division en virgule flottante par zéro, donc undefined ne joue pas un rôle ici. La réponse serait différente pour clang.
la source
La division en virgule flottante par zéro se comporte différemment de la division entière par zéro.
La norme de virgule flottante IEEE fait la différence entre + inf et -inf, tandis que les entiers ne peuvent pas stocker l'infini. La division entière par zéro est un comportement indéfini. La division en virgule flottante par zéro est définie par la norme à virgule flottante et donne + inf ou -inf.
la source