Fall 1:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0.0)<<std::endl;
}
Es wird ohne Warnungen kompiliert und gedruckt inf
. OK, C ++ kann die Division durch Null verarbeiten ( siehe live ).
Aber,
Fall 2:
#include <iostream>
int main()
{
double d = 15.50;
std::cout<<(d/0)<<std::endl;
}
Der Compiler gibt die folgende Warnung aus ( sehen Sie es live ):
warning: division by zero [-Wdiv-by-zero]
std::cout<<(d/0)<<std::endl;
Warum warnt der Compiler im zweiten Fall?
Ist 0 != 0.0
?
Bearbeiten:
#include <iostream>
int main()
{
if(0 == 0.0)
std::cout<<"Same"<<std::endl;
else
std::cout<<"Not same"<<std::endl;
}
Ausgabe:
Same
c++
gcc
floating-point
divide-by-zero
Jayesh
quelle
quelle
Antworten:
Die Gleitkommadivision durch Null ist durch IEEE gut definiert und ergibt unendlich (entweder positiv oder negativ, je nach Wert des Zählers (oder
NaN
für ± 0) ).Für Ganzzahlen gibt es keine Möglichkeit, Unendlichkeit darzustellen, und die Sprache definiert die Operation als undefiniertes Verhalten, sodass der Compiler hilfreich versucht, Sie von diesem Pfad fernzuhalten.
In diesem Fall sollte
double
der Divisor (0
) jedoch , da der Zähler a ist , ebenfalls auf ein Double heraufgestuft werden, und es gibt keinen Grund, hier eine Warnung zu geben, ohne dafür eine Warnung zu geben.0.0
Ich denke, dies ist ein Compiler-Fehler.quelle
d/0
,0
wird auf die Art von umgerechnetd
.In Standard C ++ sind beide Fälle undefiniertes Verhalten . Alles kann passieren, einschließlich der Formatierung Ihrer Festplatte. Sie sollten "return inf. Ok" oder ein anderes Verhalten nicht erwarten oder sich darauf verlassen.
Der Compiler beschließt anscheinend, in einem Fall eine Warnung zu geben und nicht in dem anderen, aber dies bedeutet nicht, dass ein Code in Ordnung ist und einer nicht. Es ist nur eine Eigenart der Generierung von Warnungen durch den Compiler.
Aus dem C ++ 17-Standard [expr.mul] / 4:
quelle
std::numeric_limits<T>::is_iec559
isttrue
, die Division durch Null fürT
nicht UB ist (und auf den meisten Plattformen ist estrue
fürdouble
undfloat
, obwohl Sie portabel sind, werden Sie müssen dies explizit mit einemif
oderif constexpr
) überprüfen .is_iec559
alstrue
bedeutet, dass die Implementierung ein Verhalten dokumentiert, das der Standard undefiniert lässt. Dies ist nur ein Fall, in dem die Dokumentation der Implementierung programmgesteuert gelesen werden kann. Nicht einmal der einzige: Gleiches giltis_modulo
für vorzeichenbehaftete Ganzzahltypen.Meine beste Vermutung, um diese spezielle Frage zu beantworten , wäre, dass der Compiler vor der Konvertierung von
int
in eine Warnung ausgibtdouble
.Die Schritte wären also wie folgt:
/(T, T2)
, woT=double
,T2=int
.std::is_integral<T2>::value
isttrue
undb == 0
- dies löst eine Warnung aus.T2
nach durchdouble
Dies ist natürlich Spekulation und basiert auf vom Compiler definierten Spezifikationen. Aus Standardsicht haben wir es mit möglichen undefinierten Verhaltensweisen zu tun.
Bitte beachten Sie, dass dies gemäß der GCC-Dokumentation erwartet wird
(übrigens scheint es, dass dieses Flag in GCC 8.1 nicht explizit verwendet werden kann).
quelle
/
zu wissen, dass es sich um eine Division handelt. Wenn die linke Seite einFoo
Objekt gewesen wäre und es ein Objekt geben würde, wäreoperator/(Foo, int)
es möglicherweise nicht einmal eine Teilung. Der Compiler kennt seine Aufteilung nur, wenn erbuilt-in / (double, double)
eine implizite Konvertierung der rechten Seite verwendet hat. Aber das bedeutet, dass es KEINE Division durch machtint(0)
, es macht eine Division durchdouble(0)
.operator /(double, int)
ist es sicherlich akzeptabel. Dann heißt es, dass die Konvertierung vor jeder anderen Aktion durchgeführt wird, aber GCC könnte eine schnelle Überprüfung durchführen, obT2
es sich um einen ganzzahligen Typ handelt,b == 0
und in diesem Fall eine Warnung ausgeben. Ich bin mir nicht sicher, ob dies vollständig standardkonform ist, aber Compiler haben die volle Freiheit, Warnungen zu definieren und wann sie ausgelöst werden sollten.operator/(double,int)
wirklich existiert. Der Compiler kann beispielsweise entscheiden, diea/b
Konstante zu optimieren,b
indem er sie durch ersetzta * (1/b)
. Das bedeutet natürlich, dass Sie nicht mehroperator/(double,double)
zur Laufzeit anrufen, sondern umso schnelleroperator*(double,double)
. Aber es ist jetzt der Optimierer1/0
, deroperator*
Ich werde in dieser Antwort nicht auf das UB / nicht UB-Debakel eingehen.
Ich möchte nur darauf hinweisen
0
und bin0.0
anders, obwohl ich0 == 0.0
als wahr bewertet habe.0
ist einint
Literal und0.0
ist eindouble
Literal.In diesem Fall ist das Endergebnis jedoch dasselbe: Es
d/0
ist eine Gleitkommadivision, dad
es doppelt ist und daher0
implizit in doppelt konvertiert wird.quelle
double
durch einint
Mittel, in dasint
konvertiert wirddouble
, und es in dem Standard angegeben ist,0
der in0.0
(conv.fpint / 2) konvertiert0
es dasselbe ist wie0.0
0 != 0.0
?". OP fragt nie, ob sie "gleich" sind. Es scheint mir auch, dass die Absicht der Frage darin besteht, obd/0
es sich anders verhalten kann alsd/0.0
Ich würde behaupten , dass
foo/0
undfoo/0.0
sind nicht gleich. Der resultierende Effekt der ersten (Ganzzahldivision oder Gleitkommadivision) hängt stark vom Typ abfoo
, während dies für die zweite nicht gilt (es handelt sich immer um eine Gleitkommadivision).Ob einer der beiden UB ist, spielt keine Rolle. Den Standard zitieren:
(Hervorhebung von mir)
Beachten Sie die Warnung " Klammern um die als Wahrheitswert verwendete Zuweisung vorschlagen ": Sie können dem Compiler mitteilen, dass Sie das Ergebnis einer Zuweisung wirklich verwenden möchten, indem Sie explizit angeben und Klammern um die Zuweisung hinzufügen. Die resultierende Anweisung hat den gleichen Effekt, teilt dem Compiler jedoch mit, dass Sie wissen, was Sie tun. Das Gleiche gilt für
foo/0.0
: Da Sie dem Compiler explizit "Dies ist eine Gleitkommadivision" mitteilen, indem Sie0.0
stattdessen verwenden0
, vertraut der Compiler Ihnen und gibt keine Warnung aus.quelle
foo
. Das ist beabsichtigt. Ihre Behauptung ist nur in dem Fall wahr, dassfoo
es sich um einen Gleitkommatyp handelt.Dies sieht aus wie ein gcc-Fehler, die Dokumentation für
-Wno-div-by-zero
sagt eindeutig :und nach den üblichen arithmetischen Konvertierungen, die in [expr.arith.conv] behandelt werden, sind beide Operanden doppelt :
und [expr.mul] :
In Bezug darauf, ob die Gleitkommadivision durch Null ein undefiniertes Verhalten ist und wie unterschiedliche Implementierungen damit umgehen, scheint mir hier meine Antwort zu sein . TL; DR; Es sieht so aus, als ob gcc mit Anhang F der Gleitkommadivision durch Null übereinstimmt, daher spielt undefiniert hier keine Rolle. Die Antwort wäre anders für Klirren.
quelle
Die Gleitkommadivision durch Null verhält sich anders als die ganzzahlige Division durch Null.
Der IEEE-Gleitkomma- Standard unterscheidet zwischen + inf und -inf, während Ganzzahlen nicht unendlich speichern können. Das Ergebnis der Ganzzahldivision durch Null ist ein undefiniertes Verhalten. Die Gleitkommadivision durch Null wird durch den Gleitkommastandard definiert und führt zu + inf oder -inf.
quelle