webentwicklung-frage-antwort-db.com.de

C ++ Warnung: Division von Double durch Null

Fall 1:

#include <iostream>

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

Es kompiliert ohne Warnungen und gibt inf aus. OK, C++ kann die Division durch Null verarbeiten ( live sehen ).

Aber,

Fall 2:

#include <iostream>

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

Der Compiler gibt die folgende Warnung aus ( live sehen ):

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
97
Jayesh

Die Gleitkommadivision durch Null ist gut definiert durch IEEE und ergibt eine Unendlichkeit (entweder positiv oder negativ, je nach dem Wert des Zählers (oder NaN für ± 0) ).

Für Ganzzahlen gibt es keine Möglichkeit, Unendlich darzustellen, und die Sprache definiert die Operation mit ndefiniertes Verhalten , sodass der Compiler hilfreich versucht, Sie von diesem Pfad abzulenken.

Da der Zähler in diesem Fall jedoch ein double ist, wird der Divisor (0) sollte auch zu einem Double befördert werden und es gibt keinen Grund, hier eine Warnung zu geben, ohne eine Warnung für 0.0 also ich denke das ist ein compiler fehler.

107
Motti

In Standard C++ sind beide Fälle ndefiniertes Verhalten . Alles kann passieren, einschließlich der Formatierung Ihrer Festplatte. Sie sollten nicht erwarten oder sich auf "return inf. Ok" oder ein anderes Verhalten verlassen.

Der Compiler entscheidet sich anscheinend dafür, in einem Fall eine Warnung zu geben und nicht im anderen, aber dies bedeutet nicht, dass ein Code in Ordnung ist und einer nicht. Es ist nur eine Eigenart der Warnungen des Compilers.

Aus dem C++ 17-Standard [expr.mul]/4:

Der binäre Operator / Liefert den Quotienten und der binäre Operator % Den Rest aus der Division des ersten Ausdrucks durch den zweiten. Wenn der zweite Operand von / Oder % Null ist, ist das Verhalten undefiniert.

42
M.M

Meine beste Vermutung, um diese spezielle Frage zu beantworten wäre, dass der Compiler eine Warnung ausgibt , bevor die Konvertierung von int durchgeführt wird. zu double.

Die Schritte wären also wie folgt:

  1. Parse Ausdruck
  2. arithmetischer Operator/(T, T2), wobei T=double, T2=int.
  3. Stellen Sie sicher, dass std::is_integral<T2>::valuetrue und b == 0 Ist - dies löst eine Warnung aus.
  4. Warnung ausstrahlen
  5. Implizite Konvertierung von T2 In double durchführen
  6. Führen Sie eine genau definierte Unterteilung durch (da sich der Compiler für die Verwendung von IEEE 754 entschieden hat).

Dies ist natürlich eine 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äß GCC-Dokumentation erwartet wird
(Übrigens scheint es, dass dieses Flag in GCC 8.1 nicht explizit verwendet werden kann)

- Div-by-Zero
Warnung vor einer Ganzzahldivision zur Kompilierungszeit durch Null. Dies ist die Standardeinstellung. Verwenden Sie -Wno-div-by-zero, um die Warnmeldungen zu unterdrücken. Gleitkommadivision durch Null wird nicht gewarnt, da dies ein legitimer Weg ist, um Unendlichkeiten und NaNs zu erhalten.

12
Yksisarvinen

Ich werde in dieser Antwort nicht auf das UB/nicht UB-Debakel eingehen.

Ich möchte nur darauf hinweisen, dass 0 und 0.0sind unterschiedlich trotz 0 == 0.0 wird mit true bewertet. 0 ist ein int Literal und 0.0 ist ein double Literal.

In diesem Fall ist das Endergebnis jedoch dasselbe: d/0 ist Gleitkommadivision, weil d doppelt ist und 0 wird implizit in double konvertiert.

9
bolov

Ich würde behaupten, dass foo/0 und foo/0.0 sind nicht gleich. Der resultierende Effekt der ersten (ganzzahlige Division oder Gleitkommadivision) hängt stark vom Typ von foo ab, während dies für die zweite nicht gilt (es wird immer eine Gleitkommadivision sein).

Ob einer der beiden UB ist, ist unerheblich. Den Standard zitieren:

Zulässiges undefiniertes Verhalten reicht vom vollständigen Ignorieren der Situation mit unvorhersehbaren Ergebnissen bis zum Verhalten während der Übersetzung oder Programmausführung auf dokumentierte Weise, die für die Umgebung charakteristisch ist (mit oder ohne Ausgabe einer Diagnosemeldung) zum Beenden einer Übersetzung oder Ausführung (mit Ausgabe einer Diagnosemeldung).

(Hervorhebung meiner)

Beachten Sie die Warnung " Klammern um die als Wahrheitswert verwendete Zuweisung vorschlagen ": Die Möglichkeit, dem Compiler mitzuteilen, dass Sie wirklich möchten Verwenden Sie das Ergebnis einer Zuweisung, indem Sie explizit angeben und Klammern um die Zuweisung einfü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 mitteilen, dass dies eine Gleitkommadivision ist, verwenden Sie 0.0 anstatt 0, der Compiler vertraut Ihnen und gibt keine Warnung aus.

7
Cássio Renan

Dies sieht aus wie ein GCC-Fehler, die Dokumentation für -Wno-div-by-zerosagt deutlich :

Warnen Sie nicht vor der Ganzzahldivision zur Kompilierungszeit durch Null. Gleitkommadivision durch Null wird nicht gewarnt, da dies ein legitimer Weg sein kann, um Unendlichkeiten und NaNs zu erhalten.

und nach Übliche arithmetische Umrechnungen behandelt in [expr.arith.conv] werden beide Operanden double:

Viele Binäroperatoren, die Operanden vom Typ Arithmetik oder Aufzählung erwarten, verursachen auf ähnliche Weise Konvertierungen und liefern Ergebnistypen. Der Zweck ist es, einen gemeinsamen Typ zu erhalten, der auch der Typ des Ergebnisses ist. Dieses Muster nennt man die üblichen arithmetischen Umrechnungen, die wie folgt definiert sind:

...

- Andernfalls wird, wenn einer der Operanden doppelt ist, der andere in doppelt umgewandelt.

und [expr.mul] :

Die Operanden von * und/oder müssen einen arithmetischen oder nicht-skopierten Aufzählungstyp haben. Die Operanden von% müssen einen ganzzahligen oder einen ungeschlossenen Aufzählungstyp haben. Die üblichen arithmetischen Umrechnungen werden an den Operanden durchgeführt und bestimmen die Art des Ergebnisses.

In Bezug darauf, ob Gleitkommadivision durch Null undefiniertes Verhalten ist und wie unterschiedlich die Implementierung damit umgeht, scheint meine Antwort hier . TL; DR; Es sieht so aus, als würde gcc Annex F bezüglich Gleitkommadivision entsprechen bei null spielt also undefined hier keine rolle, die antwort wäre für clang anders.

4
Shafik Yaghmour

Die Gleitkommadivision durch Null verhält sich anders als die Ganzzahldivision durch Null.

Der Standard IEEE-Gleitkomma unterscheidet zwischen + inf und -inf, während Ganzzahlen keine Unendlichkeit speichern können. Ganzzahlige Division durch Null ist undefiniertes Verhalten. Gleitkommadivision durch Null wird durch den Gleitkomma-Standard definiert und ergibt + inf oder -inf.

2
Rizwan