So erhalten Sie das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahl
Lesezeit: 9 Minuten
MetallicPriest
Ich habe ein Programm, das auf zwei Prozessoren läuft, von denen einer keine Fließkommaunterstützung hat. Also muss ich Gleitkommaberechnungen mit Festkomma in diesem Prozessor durchführen. Zu diesem Zweck werde ich eine Fließkomma-Emulationsbibliothek verwenden.
Ich muss zuerst die Zeichen, Mantissen und Exponenten von Gleitkommazahlen auf dem Prozessor extrahieren, die Gleitkommazahlen unterstützen. Meine Frage ist also, wie ich das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahl mit einfacher Genauigkeit erhalten kann.
Dem Format dieser Abbildung folgend,
Das habe ich bisher getan, aber außer Vorzeichen sind weder Mantisse noch Exponent korrekt. Ich glaube, mir fehlt etwas.
void getSME( int& s, int& m, int& e, float number )
{
unsigned int* ptr = (unsigned int*)&number;
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
}
Aliasing durch Zeigerkonvertierung wird vom C-Standard nicht unterstützt und kann in einigen Compilern problematisch sein. Es ist bevorzugt zu verwenden (union { float f; uint32_t u; }) { number } .u. Dies gibt a zurück uint32_t das sind die Bytes der floatnumber als 32-Bit-Ganzzahl ohne Vorzeichen neu interpretiert.
– Eric Postpischil
28. März 2013 um 15:29 Uhr
Ich gehe von IEEE 754 32-Bit-Binärdatei aus. Sind Ihnen die folgenden Probleme bekannt? (1) Der Exponent ist voreingenommen, indem 127 zum tatsächlichen Exponenten addiert wird. (2) Alle außer sehr kleinen Gleitkommazahlen werden normalisiert, und das führende 1-Bit einer normalisierten Gleitkomma-Mantisse wird nicht gespeichert.
– Patricia Shanahan
28. März 2013 um 17:05 Uhr
Meinst du C oder C++ (C hat keine Referenzen, nur Zeiger)
– Wildpässer
26. Oktober 2013 um 16:25 Uhr
Drei Probleme: 0. Entfernen des Bias vom codierten Exponenten nicht 1. Hinzufügen des impliziten Mantissenbits für normale Nicht-Null-Zahlen 2. Nichtbehandeln von Denormalen, Unendlichkeiten und sNaN/qNaNs
– Benutzer246672
22. September 2018 um 22:28 Uhr
Eran
Ich denke, es ist besser, Gewerkschaften zu verwenden, um die Besetzungen durchzuführen, es ist klarer.
„Aus irgendeinem Grund wurde dieser ursprüngliche Zweck der Union durch etwas völlig anderes „überschrieben“: Ein Mitglied einer Union zu schreiben und sie dann durch ein anderes Mitglied zu inspizieren. Diese Art der Neuinterpretation von Erinnerungen ist keine gültige Verwendung von Unions. Sie führt im Allgemeinen zu undefiniertem Verhalten.” stackoverflow.com/a/2313676/1127387
– datwelk
2. November 2013 um 13:50 Uhr
Es gibt kein Gesetz, das besagt, dass man Dinge nur für das verwenden darf, wofür sie ursprünglich geschaffen wurden. Sonst hätte das erste Flugzeug keine Fahrradteile verwendet. “Allgemein” undefiniert? Was ist mit den Gelegenheiten, wenn es definiert ist oder wenn Sie mit dem Verhalten auf einer bestimmten Plattform/Situation zufrieden sind?
– Benutzer146043
28. Februar 2014 um 11:29 Uhr
Diese Methode schlägt fehl, wenn 1) float ist nicht IEEE 754 32 Bit binär (nicht so selten) 2) unsigned ist 16-Bit (in der eingebetteten Welt üblich) 3) Endian von unsigned/float nicht übereinstimmen. (Selten). 4) Mathematische Interpretation wird verwendet für exponent/mantissa da diese Antwort den voreingenommenen Exponenten und die unvollständige Mantisse/Mantisse zeigt.
– chux – Wiedereinsetzung von Monica
5. März 2016 um 17:34 Uhr
Ist der obige Code portabel? Was passiert auf Big- und Little-Endian-Maschinen?
– Jo C
7. März 2016 um 23:39 Uhr
Sehr spät zur Party hier, aber nein, die union ist nicht besser, weil es nicht garantiert funktioniert. Es ist sicherlich nicht tragbar. Nichts zwingt die C-Implementierung dazu, die Bitfelder so anzuordnen, dass die Vereinigung sie den gewünschten Teilen der zuordnet float Darstellung, ungeachtet der gesonderten Frage, ob man sich überhaupt auf Wortwitze verlassen kann.
– Johannes Bollinger
25. Oktober 2016 um 21:27 Uhr
Mein Rat ist, sich an Regel 0 zu halten und nicht zu wiederholen, was Standardbibliotheken bereits tun, wenn dies ausreicht. Sehen Sie sich math.h (cmath in Standard-C++) und die Funktionen frexp, frexpf, frexpl an, die einen Gleitkommawert (Double, Float oder Long Double) in seinen Signifikanten- und Exponententeil zerlegen. Um das Vorzeichen aus dem Signifikanten zu extrahieren, können Sie signbit, auch in math.h / cmath, oder copysign (nur C++11) verwenden. Einige Alternativen mit etwas unterschiedlicher Semantik sind modf und ilogb/scalbn, verfügbar in C++11; http://en.cppreference.com/w/cpp/numeric/math/logb vergleicht sie, aber ich habe in der Dokumentation nicht gefunden, wie sich all diese Funktionen mit +/-inf und NaNs verhalten. Schließlich, wenn Sie wirklich Bitmasken verwenden wollen (z. B. wenn Sie unbedingt die genauen Bits kennen müssen und Ihr Programm möglicherweise verschiedene NaNs mit unterschiedlichen Darstellungen hat und Sie den obigen Funktionen nicht vertrauen), machen Sie zumindest alles plattformunabhängig indem Sie die Makros in float.h/cfloat verwenden.
Gibt es einen trinkbaren Weg, um die Existenz des versteckten Bits im Signifikanten zu erfahren? Und was ist mit der Erkennung des Exponenten-Bias?
– FrankHB
27. November 2021 um 21:02 Uhr
Alexej Frunze
Finden Sie das Format der Fließkommazahlen heraus, die auf der CPU verwendet werden, die Fließkomma direkt unterstützt, und zerlegen Sie es in diese Teile. Das gebräuchlichste Format ist IEEE-754.
Alternativ könnten Sie diese Teile mit einigen speziellen Funktionen erhalten (double frexp(double value, int *exp); und double ldexp(double x, int exp);) wie in dieser Antwort gezeigt.
Eine weitere Option ist die Verwendung %a mit printf().
Schade, dass ich auf der Suche nach einer tragbaren Lösung bin implementierendtoadas ist etwas eine Teilmenge von printf…
– FrankHB
27. November 2021 um 20:59 Uhr
Xymostech
Du bist &ing die falschen Bits. Ich denke du willst:
s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;
Denken Sie daran, wenn Sie &, nullen Sie Bits, die Sie nicht setzen. In diesem Fall möchten Sie also das Vorzeichenbit auf Null setzen, wenn Sie den Exponenten erhalten, und Sie möchten das Vorzeichenbit und den Exponenten auf Null setzen, wenn Sie die Mantisse erhalten.
Beachten Sie, dass die Masken direkt von Ihrem Bild stammen. Die Exponentenmaske sieht also so aus:
0 11111111 0000000000000000000000
und die Mantissenmaske sieht so aus:
0 00000000 11111111111111111111111
Unter Linux stellt das Paket glibc-headers einen Header bereit #include <ieee754.h> mit Definitionen von Fließkommatypen, zB:
union ieee754_double
{
double d;
/* This is the IEEE 754 double-precision format. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
/* Together these comprise the mantissa. */
unsigned int mantissa0:20;
unsigned int mantissa1:32;
#endif /* Big endian. */
#if __BYTE_ORDER == __LITTLE_ENDIAN
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:20;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif /* Little endian. */
} ieee;
/* This format makes it easier to see if a NaN is a signalling NaN. */
struct
{
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int negative:1;
unsigned int exponent:11;
unsigned int quiet_nan:1;
/* Together these comprise the mantissa. */
unsigned int mantissa0:19;
unsigned int mantissa1:32;
#else
# if __FLOAT_WORD_ORDER == __BIG_ENDIAN
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
unsigned int mantissa1:32;
# else
/* Together these comprise the mantissa. */
unsigned int mantissa1:32;
unsigned int mantissa0:19;
unsigned int quiet_nan:1;
unsigned int exponent:11;
unsigned int negative:1;
# endif
#endif
} ieee_nan;
};
#define IEEE754_DOUBLE_BIAS 0x3ff /* Added to exponent. */
Wie setzen wir diese in der Praxis ein? Wenn wir prüfen müssen, ob wir eine Nan haben, wie machen wir das?
Das sieht nach einem großartigen (gründlichen) Anfang aus, aber wäre es zu viel verlangt, nach einem kleinen Testcode zu fragen, der die Eingabe und die Ausgabe zeigt und wie die beiden funktionieren (oder nicht)?
Das sieht nach einem großartigen (gründlichen) Anfang aus, aber wäre es zu viel verlangt, nach einem kleinen Testcode zu fragen, der die Eingabe und die Ausgabe zeigt und wie die beiden funktionieren (oder nicht)?
Sieh dir das an IEEE_754_types.h Header für die zu extrahierenden Union-Typen: float, double und long double, (Endianness gehandhabt). Hier ein Auszug:
Und sehen dtoa_base.c für eine Demonstration, wie man a konvertiert double Wert in Zeichenfolgenform.
Sehen Sie sich außerdem den Abschnitt an 1.2.1.1.4.2 – Gleitkommaspeicher-Layout des C/CPP-Referenzbuches erklärt super gut und in einfachen Worten die Speicherdarstellung / das Layout aller Gleitkommatypen und wie man sie (mit Illustrationen) gemäß der tatsächlichen IEEE 754-Gleitkommaspezifikation dekodiert.
Es hat auch Links zu wirklich sehr guten Ressourcen, die noch tiefer erklären.
14162600cookie-checkSo erhalten Sie das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahlyes
Versuchen Sie, von hier aus zu beginnen: en.wikipedia.org/wiki/Single-precision_floating-point_formataber ich bin mir fast sicher, dass du das gesehen hast
– Alex
28. März 2013 um 15:02 Uhr
Aliasing durch Zeigerkonvertierung wird vom C-Standard nicht unterstützt und kann in einigen Compilern problematisch sein. Es ist bevorzugt zu verwenden
(union { float f; uint32_t u; }) { number } .u
. Dies gibt a zurückuint32_t
das sind die Bytes derfloat
number
als 32-Bit-Ganzzahl ohne Vorzeichen neu interpretiert.– Eric Postpischil
28. März 2013 um 15:29 Uhr
Ich gehe von IEEE 754 32-Bit-Binärdatei aus. Sind Ihnen die folgenden Probleme bekannt? (1) Der Exponent ist voreingenommen, indem 127 zum tatsächlichen Exponenten addiert wird. (2) Alle außer sehr kleinen Gleitkommazahlen werden normalisiert, und das führende 1-Bit einer normalisierten Gleitkomma-Mantisse wird nicht gespeichert.
– Patricia Shanahan
28. März 2013 um 17:05 Uhr
Meinst du C oder C++ (C hat keine Referenzen, nur Zeiger)
– Wildpässer
26. Oktober 2013 um 16:25 Uhr
Drei Probleme: 0. Entfernen des Bias vom codierten Exponenten nicht 1. Hinzufügen des impliziten Mantissenbits für normale Nicht-Null-Zahlen 2. Nichtbehandeln von Denormalen, Unendlichkeiten und sNaN/qNaNs
– Benutzer246672
22. September 2018 um 22:28 Uhr