Der IEEE 754-2008-Standard für Gleitkomma-Arithmetik und der ISO / IEC 10967- Standard für sprachunabhängige Arithmetik (LIA), Teil 1, beantworten, warum dies so ist.
IEEE 754 § 6.3 Das Vorzeichenbit
Wenn entweder eine Eingabe oder ein Ergebnis NaN ist, interpretiert dieser Standard das Vorzeichen eines NaN nicht. Beachten Sie jedoch, dass Operationen an Bitfolgen - copy, negate, abs, copySign - das Vorzeichenbit eines NaN-Ergebnisses angeben, manchmal basierend auf dem Vorzeichenbit eines NaN-Operanden. Das logische Prädikat totalOrder wird auch vom Vorzeichenbit eines NaN-Operanden beeinflusst. Für alle anderen Operationen gibt dieser Standard das Vorzeichenbit eines NaN-Ergebnisses nicht an, selbst wenn nur eine Eingabe NaN vorhanden ist oder wenn die NaN aus einer ungültigen Operation erzeugt wird.
Wenn weder die Eingaben noch das Ergebnis NaN sind, ist das Vorzeichen eines Produkts oder Quotienten das ausschließliche ODER der Vorzeichen der Operanden. Das Vorzeichen einer Summe oder einer Differenz x - y, die als Summe x + (−y) betrachtet wird, unterscheidet sich von höchstens einem der Vorzeichen der Addenden. und das Vorzeichen des Ergebnisses von Konvertierungen, der Quantisierungsoperation, der roundTo-Integral-Operationen und des roundToIntegralExact (siehe 5.3.1) ist das Vorzeichen des ersten oder einzigen Operanden. Diese Regeln gelten auch dann, wenn Operanden oder Ergebnisse Null oder unendlich sind.
Wenn die Summe zweier Operanden mit entgegengesetzten Vorzeichen (oder die Differenz zweier Operanden mit gleichen Vorzeichen) genau Null ist, muss das Vorzeichen dieser Summe (oder Differenz) in allen Rundungsrichtungsattributen außer roundTowardNegative +0 sein. Unter diesem Attribut muss das Vorzeichen einer exakten Nullsumme (oder Differenz) –0 sein. X + x = x - (−x) behält jedoch das gleiche Vorzeichen wie x, auch wenn x Null ist.
Der Fall der Hinzufügung
Unter dem Standardrundungsmodus (Round-to-Nearest, Krawatten-to-Even) , sehen wir , dass x+0.0
produziert x
, außer wenn x
ist -0.0
: In diesem Fall wir eine Summe von zwei Operanden mit entgegengesetzten Vorzeichen , deren Summe haben gleich Null ist , und § 6.3 Absatz 3 Regeln, die dieser Zusatz erzeugt +0.0
.
Da +0.0
es nicht bitweise mit dem Original identisch -0.0
ist und dies -0.0
ein legitimer Wert ist, der als Eingabe auftreten kann, muss der Compiler den Code eingeben, der potenzielle negative Nullen in transformiert +0.0
.
Die Zusammenfassung: Im Standardrundungsmodus in x+0.0
, wennx
- ist nicht
-0.0
, dann ist x
selbst ein akzeptabler Ausgabewert.
- ist
-0.0
, dann muss der Ausgabewert sein +0.0
, der nicht bitweise identisch ist mit -0.0
.
Der Fall der Multiplikation
Im Standardrundungsmodus tritt kein solches Problem auf x*1.0
. Wenn x
:
- ist immer eine (sub) normale Zahl
x*1.0 == x
.
- ist
+/- infinity
, dann hat das Ergebnis +/- infinity
das gleiche Vorzeichen.
ist NaN
dann nach
IEEE 754 § 6.2.3 NaN-Ausbreitung
Eine Operation, die einen NaN-Operanden zu seinem Ergebnis weitergibt und ein einzelnes NaN als Eingabe hat, sollte ein NaN mit der Nutzlast des eingegebenen NaN erzeugen, wenn es im Zielformat darstellbar ist.
was bedeutet , dass der Exponent und Mantisse (wenn auch nicht das Zeichen) von NaN*1.0
werden empfohlen , um vom Eingang unverändert NaN
. Das Zeichen ist gemäß §6.3p1 oben nicht spezifiziert, aber eine Implementierung kann spezifizieren, dass es mit der Quelle identisch ist NaN
.
- ist
+/- 0.0
, dann ist das Ergebnis ein 0
mit seinem Vorzeichenbit XORed mit dem Vorzeichenbit von 1.0
, in Übereinstimmung mit §6.3p2. Da das Vorzeichenbit von 1.0
ist 0
, bleibt der Ausgabewert gegenüber dem Eingang unverändert. Somit ist x*1.0 == x
auch dann, wenn x
eine (negative) Null ist.
Der Fall der Subtraktion
Im Standardrundungsmodus ist die Subtraktion x-0.0
ebenfalls ein No-Op, da sie äquivalent zu ist x + (-0.0)
. Wenn x
ja
- ist
NaN
, dann gelten §6.3p1 und §6.2.3 ähnlich wie für Addition und Multiplikation.
- ist
+/- infinity
, dann hat das Ergebnis +/- infinity
das gleiche Vorzeichen.
- ist immer eine (sub) normale Zahl
x-0.0 == x
.
- ist
-0.0
, dann haben wir nach §6.3p2 " [...] das Vorzeichen einer Summe oder einer Differenz x - y, die als Summe x + (−y) betrachtet wird, von höchstens einem der Vorzeichen der Addenden; ". Dies zwingt uns, -0.0
als Ergebnis von zuzuweisen (-0.0) + (-0.0)
, da -0.0
sich das Vorzeichen von keinem der Addenden unterscheidet, während +0.0
sich das Vorzeichen von zwei der Addenden unterscheidet, was gegen diese Klausel verstößt.
- ist
+0.0
, dann reduziert sich dies auf den (+0.0) + (-0.0)
oben in The Case of Addition betrachteten Additionsfall , der nach §6.3p3 zu geben gilt +0.0
.
Da in allen Fällen der Eingabewert als Ausgabe zulässig ist, ist es zulässig, x-0.0
ein No-Op und x == x-0.0
eine Tautologie zu berücksichtigen .
Wertverändernde Optimierungen
Der IEEE 754-2008 Standard hat das folgende interessante Zitat:
IEEE 754 § 10.4 Wörtliche Bedeutung und wertverändernde Optimierungen
[...]
Die folgenden wertverändernden Transformationen behalten unter anderem die wörtliche Bedeutung des Quellcodes bei:
- Anwenden der Identitätseigenschaft 0 + x, wenn x nicht Null ist und kein signalisierendes NaN ist und das Ergebnis den gleichen Exponenten wie x hat.
- Anwenden der Identitätseigenschaft 1 × x, wenn x kein signalisierendes NaN ist und das Ergebnis den gleichen Exponenten wie x hat.
- Ändern der Nutzlast oder des Vorzeichens eines leisen NaN.
- [...]
Da alle NaNs und alle Unendlichkeiten denselben Exponenten haben und das korrekt gerundete Ergebnis von x+0.0
und x*1.0
für endlich x
genau die gleiche Größe hat wie x
, ist ihr Exponent der gleiche.
sNaNs
Signalisierende NaNs sind Gleitkomma-Trap-Werte. Dies sind spezielle NaN-Werte, deren Verwendung als Gleitkommaoperand zu einer ungültigen Operationsausnahme (SIGFPE) führt. Wenn eine Schleife, die eine Ausnahme auslöst, optimiert würde, würde sich die Software nicht mehr gleich verhalten.
Wie user2357112 in den Kommentaren ausführt , lässt der C11-Standard das Verhalten der Signalisierung von NaNs ( sNaN
) explizit undefiniert , sodass der Compiler davon ausgehen kann, dass sie nicht auftreten und die von ihnen ausgelösten Ausnahmen auch nicht auftreten. Der C ++ 11-Standard lässt die Beschreibung eines Verhaltens zum Signalisieren von NaNs aus und lässt es daher auch undefiniert.
Rundungsmodi
In alternativen Rundungsmodi können sich die zulässigen Optimierungen ändern. Beispielsweise wird im Modus " Rund auf Negativ-Unendlich" die Optimierung x+0.0 -> x
zulässig, jedoch x-0.0 -> x
verboten.
Um zu verhindern, dass GCC Standardrundungsmodi und -verhalten annimmt, kann das experimentelle Flag -frounding-math
an GCC übergeben werden.
Fazit
Clang und GCC-O3
bleiben auch bei IEEE-754-konform. Dies bedeutet, dass die oben genannten Regeln des IEEE-754-Standards eingehalten werden müssen. x+0.0
ist nicht Bit-identisch zu x
allen für x
unter diesen Regeln, aber x*1.0
so gewählt werden kann : Nämlich, wenn wir
- Befolgen Sie die Empfehlung, die Nutzlast
x
eines NaN unverändert weiterzugeben .
- Lassen Sie das Vorzeichenbit eines NaN-Ergebnisses um unverändert
* 1.0
.
- Befolgen Sie die Anweisung, das Vorzeichenbit während eines Quotienten / Produkts zu XOREN , wenn
x
es sich nicht um ein NaN handelt.
Um die IEEE-754-unsichere Optimierung zu aktivieren (x+0.0) -> x
, muss das Flag -ffast-math
an Clang oder GCC übergeben werden.