5 Stimmen

Berechnen der Bereiche von Gleitkomma-Datentypen

Ist es möglich, die Bereiche der Datentypen float, double und long double auf eine tragbare Weise zu berechnen, ohne float.h zu lesen und ANSI C zu verwenden? Unter tragbar verstehe ich auch die Fälle, in denen die Zielmaschine nicht dem IEEE 754-Standard entspricht.

Ich lese das K&R-Buch und Übung 2-1 fordert mich auf, sie "zu berechnen", also nehme ich an, dass dies bedeutet, float.h vollständig zu vermeiden, was FLT_MIN, FLT_MAX, DBL_MIN und DBL_MAX einschließt (das direkte Lesen dieser Werte würde sicherlich nicht als "Berechnung" gelten).

11voto

Christoph Punkte 157217

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.

4voto

Adam Rosenfield Punkte 373807

Für 99,99% aller Anwendungen sollten Sie von IEEE 754 ausgehen und die in definierten Konstanten verwenden. In den anderen 0,01% arbeiten Sie mit sehr spezialisierter Hardware, und in diesem Fall sollten Sie basierend auf der Hardware wissen, was zu verwenden ist.

3voto

Jonathan Leffler Punkte 694013

Unter dem Risiko einer überflüssigen Antwort:

Nein. Es gibt keinen tragbaren Weg, um die Bereiche zu berechnen. Deshalb wird der -Header bereitgestellt - weil es keinen tragbaren Weg gibt, um die darin enthaltenen Informationen abzuleiten.

2voto

Sophie Alpert Punkte 133000

Sie könnten versuchen, ein float größer zu machen, bis es überläuft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X