it-swarm.com.de

Wie man das Vorzeichen, die Mantisse und den Exponenten einer Gleitkommazahl erhält

Ich habe ein Programm, das auf zwei Prozessoren läuft, von denen einer keine Floating-Point-Unterstützung hat. Ich muss also Fließkomma-Berechnungen unter Verwendung von Festpunkten 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 Figur folgend, 

enter image description hereDas habe ich bisher getan, aber außer dem Zeichen sind weder Mantisse noch Exponent richtig. Ich denke, 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;
}
35
MetallicPriest

Ich denke, es ist besser, Gewerkschaften für die Besetzung einzusetzen, das 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

20
eran

Ermitteln Sie das Format der Gleitkommazahlen, die in der CPU verwendet werden und direkt Gleitkommazahlen unterstützen, und unterteilen Sie sie in diese Teile. Das gebräuchlichste Format ist IEEE-754 .

Alternativ können Sie diese Teile mit einigen speziellen Funktionen (double frexp(double value, int *exp); und double ldexp(double x, int exp);) erhalten, wie in dieser Antwort gezeigt.

Eine weitere Option ist die Verwendung von %a mit printf().

18
Alexey Frunze

Mein Rat ist, die Regel 0 einzuhalten und nicht erneut zu wiederholen, was Standardbibliotheken bereits tun, wenn dies ausreicht. Schauen Sie sich math.h (cmath in Standard C++) und die Funktionen frexp, frexpf, frexpl an, die einen Fließkommawert (double, float oder long double) in ihrem signifikanten und exponentiellen Teil unterbrechen. Um das Zeichen aus dem signifikanten Wert zu extrahieren, können Sie signbit auch in math.h/cmath oder copysign (nur C++ 11) verwenden. Einige Alternativen mit etwas anderer 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. Wenn Sie wirklich Bitmasken verwenden möchten (z. B. müssen Sie unbedingt die genauen Bits kennen und Ihr Programm möglicherweise unterschiedliche NaNs mit unterschiedlichen Repräsentationen haben und Sie den oben genannten Funktionen nicht vertrauen), machen Sie zumindest alles plattformunabhängig mit den Makros in float.h/cfloat.

15
Pietro Braione

Sie &ing die falschen Bits. Ich denke du willst:

s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;

Denken Sie daran, dass Sie, wenn Sie & sind, Bits auf Null setzen, die Sie nicht gesetzt haben. 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 00000000000000000000000

und die Mantissenmaske sieht so aus:

0 00000000 11111111111111111111111

7
Xymostech

Im Linux-Paket enthält glibc-headers Header #include <ieee754.h> mit Definitionen von Gleitkommatypen, z. B .:

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.  */
6
  1. Erstellen Sie keine Funktionen, die mehrere Funktionen erfüllen.
  2. Nicht maskieren, dann verschieben; verschiebe dann die Maske.
  3. Ändern Sie die Werte nicht unnötig, da sie langsam sind, den Cache zerstören und fehleranfällig sind.
  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. */
0
Barry

Dies funktioniert gut für den Datentyp double:

double n = 923842342.3423452;

int  sign       = *(uintmax_t *)&n >> 63;
int  exp        = (*(uintmax_t *)&n << 1 >> 53) - BIAS; /* i.e 1023 */
long mantissa   = *(uintmax_t *)&n << 12 >> 12;

Wenn Sie versuchen, sie in Binär/Dezimal zu drucken, erhalten Sie Folgendes:

In Binary:   0  0b00000011101  0b1011100010000101101110010011001010111101000111111000
In Decimal:  0             29                                        3246151636341240
0
AymenTM