So erhalten Sie das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahl

Lesezeit: 9 Minuten

Benutzeravatar von MetallicPriest
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,

Geben Sie hier die Bildbeschreibung ein
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;
}

  • 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ück uint32_t das sind die Bytes der float 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

Benutzeravatar von eran
Eran

Ich denke, es ist besser, Gewerkschaften zu verwenden, um die Besetzungen durchzuführen, es ist klarer.

#include <stdio.h>

typedef union {
  float f;
  struct {
    unsigned int mantisa : 23;
    unsigned int exponent : 8;
    unsigned int sign : 1;
  } parts;
} float_cast;

int main(void) {
  float_cast d1 = { .f = 0.15625 };
  printf("sign = %x\n", d1.parts.sign);
  printf("exponent = %x\n", d1.parts.exponent);
  printf("mantisa = %x\n", d1.parts.mantisa);
}

Beispiel basierend auf http://en.wikipedia.org/wiki/Single_precision

  • „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

Benutzeravatar von Alexey Frunze
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 implementieren dtoadas ist etwas eine Teilmenge von printf

    – FrankHB

    27. November 2021 um 20:59 Uhr

Benutzeravatar von Xymostech
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?

    – Dimitri Lesnoff

    16. Juni um 16:40 Uhr

  • @DimitriLesnoff Beginnen Sie mit std::isnan.

    – Maxim Egorushkin

    19. Juni um 22:46 Uhr

  • 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)?

    – James Bush

    10. Juli um 3:36

  • @JamesBush godbolt.org/z/EzbY5jnTE

    – Maxim Egorushkin

    10. Juli um 13:10 Uhr

  1. Erstellen Sie keine Funktionen, die mehrere Dinge tun.
  2. Nicht maskieren, dann verschieben; verschieben, dann maskieren.
  3. Verändern Sie Werte nicht unnötigerweise, da dies langsam, Cache-zerstörend und fehleranfällig ist.
  4. Verwenden Sie keine magischen Zahlen.
/* NaNs, infinities, denormals unhandled */
/* assumes sizeof(float) == 4 and uses ieee754 binary32 format */
/* assumes two's-complement machine */
/* C99 */
#include <stdint.h>

#define SIGN(f) (((f) <= -0.0) ? 1 : 0)

#define AS_U32(f) (*(const uint32_t*)&(f))
#define FLOAT_EXPONENT_WIDTH 8
#define FLOAT_MANTISSA_WIDTH 23
#define FLOAT_BIAS ((1<<(FLOAT_EXPONENT_WIDTH-1))-1) /* 2^(e-1)-1 */
#define MASK(width)  ((1<<(width))-1) /* 2^w - 1 */
#define FLOAT_IMPLICIT_MANTISSA_BIT (1<<FLOAT_MANTISSA_WIDTH)

/* correct exponent with bias removed */
int float_exponent(float f) {
  return (int)((AS_U32(f) >> FLOAT_MANTISSA_WIDTH) & MASK(FLOAT_EXPONENT_WIDTH)) - FLOAT_BIAS;
}

/* of non-zero, normal floats only */
int float_mantissa(float f) {
  return (int)(AS_U32(f) & MASK(FLOAT_MANTISSA_BITS)) | FLOAT_IMPLICIT_MANTISSA_BIT;
}

/* Hacker's Delight book is your friend. */

  • Wie setzen wir diese in der Praxis ein? Wenn wir prüfen müssen, ob wir eine Nan haben, wie machen wir das?

    – Dimitri Lesnoff

    16. Juni um 16:40 Uhr

  • @DimitriLesnoff Beginnen Sie mit std::isnan.

    – Maxim Egorushkin

    19. Juni um 22:46 Uhr

  • 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)?

    – James Busch

    10. Juli um 3:36

  • @JamesBush godbolt.org/z/EzbY5jnTE

    – Maxim Egorushkin

    10. Juli um 13:10 Uhr

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:

/*
** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
**  Single Precision (float)  --  Standard IEEE 754 Floating-point Specification
*/

# define IEEE_754_FLOAT_MANTISSA_BITS (23)
# define IEEE_754_FLOAT_EXPONENT_BITS (8)
# define IEEE_754_FLOAT_SIGN_BITS     (1)

.
.
.

# if (IS_BIG_ENDIAN == 1)
    typedef union {
        float value;
        struct {
            __int8_t   sign     : IEEE_754_FLOAT_SIGN_BITS;
            __int8_t   exponent : IEEE_754_FLOAT_EXPONENT_BITS;
            __uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
        };
    } IEEE_754_float;
# else
    typedef union {
        float value;
        struct {
            __uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
            __int8_t   exponent : IEEE_754_FLOAT_EXPONENT_BITS;
            __int8_t   sign     : IEEE_754_FLOAT_SIGN_BITS;
        };
    } IEEE_754_float;
# endif

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.

1416260cookie-checkSo erhalten Sie das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahl

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy