Eine schnelle Methode, um ein Double zu einem 32-Bit-Int zu runden, wird erläutert
Beim Lesen Lua source code, habe ich gemerkt, dass Lua verwendet eine macro
um eine double
zu einem 32-bit -int
. Ich extrahiert die macro
und es sieht wie folgt aus:
union i_cast {double d; int i[2]};
#define double2int(i, d, t) \
{volatile union i_cast u; u.d = (d) + 6755399441055744.0; \
(i) = (t)u.i[ENDIANLOC];}
Hier ENDIANLOC
ist definiert als endianness0
für little-endian, 1
für big endian. Lua sorgfältig verarbeitet endianness. t
steht für den integer-Typ, wie int
oder unsigned int
.
Ich habe ein wenig Forschung und es gibt eine einfachere format macro
verwendet den gleichen Gedanken:
#define double2int(i, d) \
{double t = ((d) + 6755399441055744.0); i = *((int *)(&t));}
Oder in einem C++-style:
inline int double2int(double d)
{
d += 6755399441055744.0;
return reinterpret_cast<int&>(d);
}
Dieser trick kann funktionieren, auf jedem Rechner mit IEEE 754 (was bedeutet, dass so ziemlich jeder Maschine heute). Es funktioniert sowohl für positive und negative zahlen, und die Rundung folgt Banker ' s Rule. (Dies ist nicht überraschend, denn es folgt IEEE 754.)
Ich schrieb ein wenig Programm, um es zu testen:
int main()
{
double d = -12345678.9;
int i;
double2int(i, d)
printf("%d\n", i);
return 0;
}
Ausgibt -12345679, wie erwartet.
Ich würde gerne ins detail, wie diese heikle macro
funktioniert. Die Magische Zahl 6755399441055744.0
ist eigentlich 2^51 + 2^52
oder 1.5 * 2^52
und 1.5
im Binär dargestellt werden kann, wie 1.1
. Wenn jeder 32-bit-Ganzzahl wird Hinzugefügt, diese Magische Zahl, nun, ich bin verloren hier. Wie funktioniert dieser trick funktioniert?
P. S: Das ist in der Lua-Quellcode, Llimits.h.
UPDATE:
- Wie @Mysticial Punkte heraus, bei dieser Methode nicht begrenzen sich auf ein 32-bit -
int
es kann auch erweitert werden, um eine 64-bit -int
solange die Zahl ist in
der Wertebereich von 2^52. (Diemacro
braucht einige änderungen.) - Einige Materialien, die sagen, diese Methode kann nicht verwendet werden,Direct3D.
-
Beim arbeiten mit Microsoft assembler für x86, es ist ein noch
schnellermacro
geschriebenassembly
(dies ist auch extrahiert aus Lua-Quelle):#define double2int(i,n) __asm {__asm fld n __asm fistp i}
-
Gibt es eine ähnliche Magische Zahl für single-precision-Nummer:
1.5 * 2 ^23
InformationsquelleAutor der Frage Yu Hao | 2013-06-11
Du musst angemeldet sein, um einen Kommentar abzugeben.
Einen
double
vertreten, wie diese:und es kann gesehen werden als zwei 32-bit-Ganzzahlen; nun, die
int
genommen in allen Versionen des Codes (angenommen es ist ein 32-bit -int
) ist die auf der rechten Seite in der Figur, so was Sie tut, in der end ist einfach nur die untersten 32 bits der Mantisse.Nun, die Magische Zahl, wie du richtig angegeben, 6755399441055744 ist 2^51 + 2^52; hinzufügen, wie eine Reihe Kräfte, die
double
zu gehen, in die "süße range" zwischen 2^52 und 2^53, welcher, wie erläutert durch Wikipedia hierhat eine interessante Eigenschaft:Dies folgt aus der Tatsache, dass die Mantisse 52 bit breit.
Andere interessante Tatsache über das hinzufügen von 251+252 ist, es betrifft die Mantisse nur in den zwei höchsten bits - die verworfen, auf jeden Fall, denn wir nehmen nur den niedrigsten 32 bit.
Last but not least: das Zeichen.
IEEE 754 floating-point verwendet Größenordnung und Vorzeichen-Darstellung, während die ganzen zahlen auf "normalen" Maschinen mit 2-Komplement Arithmetik; wie wird dies gehandhabt hier?
Wir unterhielten uns nur über positive ganze zahlen sind; nehmen wir nun an haben wir es mit einer negativen Zahl darstellbar durch einen 32-bit -
int
also weniger (in absoluten Wert) als (-2^31+1); nennen Sie es-a
. Eine solche Zahl ist natürlich positiv, indem die Magische Zahl, und der daraus resultierende Wert 252+251+(-a).Nun, was kriegen wir, wenn wir interpretieren die Mantisse in 2-Komplement-Darstellung? Es muss das Ergebnis der 2-Komplement-Summe von (252+251) und (-a). Wieder der erste Begriff betrifft nur die oberen zwei bits, was bleibt in den bits 0~50 ist der 2-Komplement-Darstellung (-a) (wieder, minus die oberen zwei bits).
Da die Reduktion eines 2-Komplement-Zahl auf eine kleinere Breite erfolgt nur durch schneiden entfernt die zusätzlichen bits auf der linken Seite, wobei die unteren 32 bits gibt, die uns richtig (-a) in 32-bit, 2-Komplement Arithmetik.
InformationsquelleAutor der Antwort Matteo Italia