Wie @Angew hervorhob , benötigt der !=Bediener auf beiden Seiten den gleichen Typ.
(float)i != iführt zu einer Förderung der RHS, um auch zu schweben, so haben wir (float)i != (float)i.
g ++ generiert auch eine Endlosschleife, optimiert jedoch nicht die Arbeit von innen heraus. Sie können sehen, dass es int-> float mit konvertiert cvtsi2ssund mit sich selbst ucomiss xmm0,xmm0vergleicht (float)i. (Das war Ihr erster Hinweis darauf, dass Ihre C ++ - Quelle nicht das bedeutet, was Sie für die Antwort von @ Angew gehalten haben.)
x != xist nur wahr, wenn es "ungeordnet" ist, weil xNaN war. ( INFINITYVergleiche in der IEEE-Mathematik gleich, aber NaN nicht. NAN == NANist falsch, NAN != NANist wahr).
gcc7.4 und älter optimiert Ihren Code korrekt jnpals Schleifenzweig ( https://godbolt.org/z/fyOhW1 ): Führen Sie eine Schleife durch, solange die Operanden x != x nicht NaN waren. (gcc8 und höher prüfen auch, ob jedie Schleife unterbrochen wurde, und können nicht optimieren, da dies für alle Nicht-NaN-Eingaben immer der Fall ist.) x86 FP vergleicht den eingestellten PF mit ungeordnet.
Übrigens, das bedeutet, dass die Optimierung von clang auch sicher ist : Es muss nur CSE (float)i != (implicit conversion to float)ials gleich sein und beweisen, dass i -> floates für den möglichen Bereich von niemals NaN ist int.
(Obwohl diese Schleife UB mit vorzeichenbehaftetem Überlauf trifft, darf sie buchstäblich jeden gewünschten Asm ausgeben, einschließlich einer ud2illegalen Anweisung oder einer leeren Endlosschleife, unabhängig davon, was der Schleifenkörper tatsächlich war.) Ignoriert jedoch den UB mit vorzeichenbehaftetem Überlauf ist diese Optimierung noch zu 100% legal.
GCC kann den Schleifenkörper nicht optimieren, selbst wenn der -fwrapvÜberlauf mit vorzeichenbehafteten Ganzzahlen genau definiert ist (als 2er-Komplement-Wraparound). https://godbolt.org/z/t9A8t_
Auch das Aktivieren -fno-trapping-mathhilft nicht. (Die Standardeinstellung von GCC ist leider die Aktivierung
-ftrapping-math, obwohl die Implementierung von GCC fehlerhaft / fehlerhaft ist .) Die Int-> Float-Konvertierung kann eine ungenaue FP-Ausnahme verursachen (für Zahlen, die zu groß sind, um genau dargestellt zu werden). Mit möglicherweise nicht maskierten Ausnahmen ist es daher sinnvoll, dies nicht zu tun Optimieren Sie den Schleifenkörper. (Da die Konvertierung 16777217in Float einen beobachtbaren Nebeneffekt haben kann, wenn die ungenaue Ausnahme nicht maskiert ist.)
Aber mit -O3 -fwrapv -fno-trapping-math, es ist 100% verpasste Optimierung, dies nicht zu einer leeren Endlosschleife zu kompilieren. Ohne #pragma STDC FENV_ACCESS ONist der Status der Sticky-Flags, die maskierte FP-Ausnahmen aufzeichnen, kein beobachtbarer Nebeneffekt des Codes. Nein int-> floatKonvertierung kann zu NaN führen, x != xkann also nicht wahr sein.
Diese Compiler sind alle für C ++ - Implementierungen optimiert, die IEEE 754 Single-Precision (Binary32) floatund 32-Bit verwenden int.
Die Bugfixed-(int)(float)i != i Schleife hätte UB in C ++ - Implementierungen mit schmalem 16-Bit intund / oder breiter float, da Sie vor dem Erreichen der ersten Ganzzahl, die nicht genau als dargestellt werden konnte, den Überlauf UB mit vorzeichenbehafteten Ganzzahlen erreichen würden float.
UB unter einer anderen Reihe von implementierungsdefinierten Auswahlmöglichkeiten hat jedoch keine negativen Konsequenzen beim Kompilieren für eine Implementierung wie gcc oder clang mit dem x86-64 System V ABI.
Übrigens können Sie das Ergebnis dieser Schleife statisch aus FLT_RADIXund berechnen FLT_MANT_DIG, definiert in <climits>. Zumindest können Sie dies theoretisch floattun , wenn es tatsächlich zum Modell eines IEEE-Floats passt und nicht zu einer anderen Art der Darstellung reeller Zahlen wie Posit / Unum.
Ich bin mir nicht sicher, wie sehr sich der ISO C ++ - Standard auf das floatVerhalten auswirkt und ob ein Format, das nicht auf Exponentenfeldern mit fester Breite und Signifikantenfeldern basiert, standardkonform wäre.
In Kommentaren:
@geza Ich würde mich freuen, die resultierende Nummer zu hören!
@nada: Es ist 16777216
Wollen Sie behaupten, Sie hätten diese Schleife zum Drucken / Zurückgeben 16777216?
Update: Da dieser Kommentar gelöscht wurde, denke ich nicht. Wahrscheinlich zitiert das OP nur die floatvor der ersten Ganzzahl, die nicht genau als 32-Bit dargestellt werden kann float. https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Precision_limits_on_integer_values, dh was sie mit diesem fehlerhaften Code überprüfen wollten.
Die Bugfixed-Version würde natürlich drucken 16777217, die erste Ganzzahl, die nicht genau darstellbar ist, und nicht der Wert davor.
(Alle höheren Float-Werte sind exakte Ganzzahlen, aber sie sind Vielfache von 2, dann 4, dann 8 usw. für Exponentenwerte, die höher als die Signifikantenbreite sind. Viele höhere Ganzzahlwerte können dargestellt werden, aber 1 Einheit an letzter Stelle (des Signifikanten) ist größer als 1, es handelt sich also nicht um zusammenhängende ganze Zahlen. Die größte Endlichkeit floatliegt knapp unter 2 ^ 128, was für gerade zu groß ist int64_t.)
Wenn ein Compiler die ursprüngliche Schleife verlassen und diese drucken würde, wäre dies ein Compiler-Fehler.