Es ist möglich (zumindest für IEEE 754 float
und double
Werte), den größten Gleitkommawert über (Pseudocode) zu berechnen:
~(-1.0) | 0.5
Bevor wir unsere Bit-Verbiegung durchführen können, müssen wir die Gleitkommawerte in Ganzzahlen umwandeln und dann wieder zurück. Dies kann auf folgende Weise erfolgen:
uint64_t m_one, half;
double max;
*(double *)(void *)&m_one = -1.0;
*(double *)(void *)&half = 0.5;
*(uint64_t *)(void *)&max = ~m_one | half;
Wie funktioniert das also? Dazu müssen wir wissen, wie die Gleitkommawerte codiert werden.
Das höchste Bit codiert das Vorzeichen, die nächsten k
Bits codieren den Exponenten und die unteren Bits enthalten den Bruchteil. Für Potenzen von 2
ist der Bruchteil 0
.
Der Exponent wird mit einem Bias (Versatz) von 2**(k-1) - 1
gespeichert, was bedeutet, dass ein Exponent von 0
einem Muster entspricht, bei dem alle bis auf das höchste Bit gesetzt sind.
Es gibt zwei Exponenten-Bitmuster mit besonderer Bedeutung:
- wenn kein Bit gesetzt ist, ist der Wert
0
, wenn der Bruchteil null ist; ansonsten ist der Wert ein Subnormal
- wenn alle Bits gesetzt sind, ist der Wert entweder
infinity
oder NaN
Dies bedeutet, dass der größte reguläre Exponent über das Setzen aller Bits außer dem niedrigsten codiert wird, was einem Wert von 2**k - 2
oder 2**(k-1) - 1
Für double
Werte, k = 11
, dh der höchste Exponent wird 1023
sein, daher ist der größte Gleitkommawert der Ordnung 2**1023
, was ungefähr 1E+308
entspricht.
Der größte Wert wird haben:
- das Vorzeichenbit auf
0
gesetzt
- alle außer dem niedrigsten Exponentenbit auf
1
gesetzt
- alle Bruchteilbits auf
1
gesetzt
Jetzt ist es möglich zu verstehen, wie unsere Magiezahlen funktionieren:
-1.0
hat sein Vorzeichenbit gesetzt, der Exponent ist der Bias - dh alle außer dem höchsten Bit sind vorhanden - und der Bruchteilteil ist 0
~(-1.0)
hat nur das höchste Exponentenbit und alle Bruchteilbits gesetzt
0.5
hat ein Vorzeichenbit und einen Bruchteil von 0
; der Exponent wird der Bias minus 1
sein, dh alle außer dem höchsten und niedrigsten Exponentenbits werden vorhanden sein
Wenn wir diese beiden Werte durch logisches Oder kombinieren, erhalten wir das gewünschte Bitmuster.
Die Berechnung funktioniert auch für x86 80-Bit erweiterte Genauigkeitswerte (auch bekannt als long double
), aber die Bit-Verbiegung muss byteweise erfolgen, da es auf 32-Bit-Hardware keinen Integer-Typ gibt, der die Werte halten könnte.
Der Bias muss tatsächlich nicht 2**(k-1) - 1
sein - es funktioniert für einen beliebigen Bias, solange er ungerade ist. Der Bias muss ungerade sein, weil sonst die Bitmuster für den Exponenten von 1.0
und 0.5
an anderen Stellen als dem niedrigsten Bit unterschiedlich sind.
Wenn die Basis b
(auch Radix genannt) des Gleitkommawerts nicht 2
ist, muss b**(-1)
anstelle von 0.5 = 2**(-1)
verwendet werden.
Wenn der größte Exponentenwert nicht reserviert ist, verwenden Sie 1.0
anstelle von 0.5
. Dies funktioniert unabhängig von Basis oder Bias (dh es ist nicht mehr auf ungerade Werte beschränkt). Der Unterschied bei der Verwendung von 1.0
besteht darin, dass das niedrigste Exponentenbit nicht gelöscht wird.
Zusammenfassend:
~(-1.0) | 0.5
funktioniert solange die Basis 2
ist, der Bias ungerade ist und der höchste Exponent reserviert ist.
~(-1.0) | 1.0
funktioniert für jede Basis oder jeden Bias, solange der höchste Exponent nicht reserviert ist.