Wenn ich den folgenden Code in VC ++ 2013 (32-Bit, keine Optimierungen) ausführen:
%Vor%Ich bekomme
%Vor%im Debugger.
Ich bin verrückt. Kann mir bitte jemand erklären wie die heck y1
und y2
unterschiedliche Werte haben?
Dies scheint nur unter /Arch:SSE2
oder /Arch:AVX
, nicht /Arch:IA32
oder /Arch:SSE
zu geschehen.
Sie konvertieren out-of-range Werte für double
in unsigned long long
. Dies ist im Standard C ++ nicht erlaubt, und Visual C ++ scheint es im SSE2-Modus schlecht zu behandeln: Es hinterlässt eine Zahl auf dem FPU-Stack, überläuft sie schließlich und macht später Code, der die FPU verwendet, fehlgeschlagen wirklich interessante Wege.
Ein reduziertes Sample ist
%Vor% Dies wird abgebrochen, wenn ull
acht oder mehr Elemente hat, aber besteht, wenn es bis zu sieben hat.
Die Lösung besteht nicht darin, Fließkommawerte in einen ganzzahligen Typ zu konvertieren, es sei denn, Sie wissen, dass der Wert im Bereich liegt.
4.9 Floating-Integral-Konvertierungen [conv.fpint]
Ein Prvalue eines Fließkommatyps kann in einen prvalue eines Integer-Typs konvertiert werden. Die Konvertierung wird abgebrochen. das heißt, der Bruchteil wird verworfen. Das Verhalten ist nicht definiert, wenn der abgeschnittene Wert nicht im Zieltyp dargestellt werden kann. [ Hinweis: Wenn der Zieltyp
bool
ist, siehe 4.12. - Endnote ]
Die Regel, dass außerhalb des Bereichs liegende Werte beim Konvertieren in einen vorzeichenlosen Typ umbrochen werden, gilt nur, wenn der Wert eines Integer-Typs bereits vorhanden ist.
Was auch immer es wert ist, scheint jedoch nicht beabsichtigt zu sein. Obwohl der Standard dieses Verhalten zulässt, kann es dennoch sinnvoll sein, dies als Fehler zu melden.
9223372036854775808
ist 0x8000000000000000
; das heißt, es ist INT64_MIN
, das auf uint64_t
umgewandelt wird.
Es sieht so aus, als würde Ihr Compiler den Rückgabewert von floor
an long long
übergeben und dann dieses Ergebnis an unsigned long long
übergeben.
Beachten Sie, dass es für einen Überlauf bei der Konvertierung von Gleitkomma in Ganzzahl recht üblich ist, den am wenigsten darstellbaren Wert zu erhalten (z. B. cvttsd2siq
auf x86-64):
Wenn eine Konvertierung ungenau ist, wird ein abgeschnittenes Ergebnis zurückgegeben. Wenn ein konvertiertes Ergebnis größer als die maximal signierte Doppelwort-Ganzzahl ist, wird die ungültige Gleitkommaausnahme ausgelöst, und wenn diese Ausnahme maskiert wird, wird der unbegrenzte Ganzzahlwert (80000000H) zurückgegeben.
(dies stammt aus der Doppelwort-Dokumentation, aber das Vierwort-Verhalten ist dasselbe.)
Hypothese: Es ist ein Fehler. Der Compiler konvertiert double
korrekt in unsigned long long
, konvertiert aber Gleitkommazahl mit erweiterter Genauigkeit (möglicherweise long double
) in unsigned long long
falsch. Details:
Dies berechnet den Wert auf der rechten Seite und speichert ihn in x
. Der Wert auf der rechten Seite könnte mit erweiterter Genauigkeit berechnet werden, aber er wird, wie die Regeln von C ++ erfordern, in double
konvertiert, wenn er in x
gespeichert wird. Der exakte mathematische Wert wäre 9710908999008998870, aber das Runden auf das double
-Format ergibt 9710908999008999424.
Dies konvertiert den double
-Wert in x
in unsigned long long
und ergibt die erwartete 9710908999008999424.
Dies berechnet den Wert auf der rechten Seite mit erweiterter Genauigkeit und erzeugt 9710908999008998870. Wenn der Wert für die erweiterte Genauigkeit in unsigned long long
konvertiert wird, gibt es einen Fehler, der 2 63 (9223372036854775808 ). Dieser Wert ist wahrscheinlich der Fehlerwert "außerhalb des Bereichs", der von einem Befehl erzeugt wird, der das Format mit erweiterter Genauigkeit in eine 64-Bit-Ganzzahl konvertiert. Der Compiler hat eine falsche Befehlssequenz verwendet, um das Format für die erweiterte Genauigkeit in unsigned long long
zu konvertieren.
Du hast y1 als Doppelgegner gecasted, bevor du es erneut zu einem langen Wurf verwandelst. Der Wert von x ist nicht der "floor" -Wert, sondern ein gerundeter Wert für floor.
Die gleiche Logik würde bei Casting-Integern und -Floats gelten. float x = (float) ((int) 1.5) gibt float x = 1.5
einen anderen WertTags und Links c++ visual-c++ unsigned-long-long-int floating-point double