Comment obtenir le signe, mantissa et exposant d'un nombre de point flottant

j'ai un programme qui fonctionne sur deux processeurs, dont l'un n'a pas de virgule flottante de soutien. Donc, j'ai besoin d'effectuer des calculs à virgule flottante en utilisant un point fixe dans ce processeur. Pour cela, j'utiliserai une bibliothèque d'émulation à virgule flottante.

je dois d'abord extraire les signes, mantissas et exposants des nombres à virgule flottante sur le processeur qui soutiennent effectivement la virgule flottante. Donc, ma question Est Comment puis-je obtenir le signe, mantissa et exposant d'un simple précision à virgule flottante nombre.

suivant le format de cette figure,

enter image description here C'est ce que j'ai fait jusqu'à présent, mais à part sign, ni mantissa ni l'exposant ne sont corrects. Je pense, je suis en manque de quelque chose.

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
demandé sur MetallicPriest 2013-03-28 19:00:54

7 réponses

je pense qu'il est préférable d'utiliser des syndicats pour faire le jette, il est plus clair.

#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);
}

exemple basé sur http://en.wikipedia.org/wiki/Single_precision

20
répondu eran 2017-06-28 02:08:01

Découvrez le format des nombres à virgule flottante utilisés sur le CPU qui supporte directement la virgule flottante et décomposez-le en ces parties. Le format le plus commun est IEEE-754.

alternativement, vous pouvez obtenir ces pièces en utilisant quelques fonctions spéciales (double frexp(double value, int *exp); et double ldexp(double x, int exp);) comme indiqué dans le cette réponse.

une autre option est d'utiliser %aprintf().

18
répondu Alexey Frunze 2017-05-23 12:18:10

mon conseil est de s'en tenir à la règle 0 et de ne pas refaire ce que les bibliothèques standards font déjà, si cela est suffisant. Regardez les mathématiques.h (cmath en standard C++) et les fonctions frexp, frexpf, frexpl, qui cassent une valeur de virgule flottante (double, float, ou long double) dans sa partie significand et exposant. Pour extraire le signe de la significande vous pouvez utiliser signbit, aussi en mathématiques.h / cmath, ou copysign (seulement C++11). Certaines alternatives, avec une sémantique plus légère, sont modf et ilogb / scalbn, disponibles en C++11; http://en.cppreference.com/w/cpp/numeric/math/logb les compare, mais je n'ai pas trouvé dans la documentation comment toutes ces fonctions se comportent avec +/-inf et NaNs. Enfin, si vous voulez vraiment utiliser des bitmasks (par exemple, vous avez désespérément besoin de connaître les bits exacts, et votre programme peut avoir des NaNs différents avec des représentations différentes, et vous ne faites pas confiance aux fonctions ci-dessus), au moins tout rendre indépendant de la plate-forme en utilisant les macros dans float.h / cfloat.

15
répondu Pietro Braione 2013-10-26 16:12:47

Vous êtes &ing le mauvais bits. Je pense que vous voulez:

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

Rappelez-vous, lorsque vous &, vous mettez à zéro les bits que vous ne mettez pas. Donc dans ce cas, vous souhaitez remettre à zéro le bit de signe lorsque vous obtenez l'exposant et vous souhaitez remettre à zéro le bit de signe et l'exposant lorsque vous obtenez la mantisse.

notez que les masques proviennent directement de votre photo. Ainsi, le masque exposant ressemblera à:

0 11111111 00000000000000000000000

et le masque de mantissa ressemblera à:

00000 00000000 1111111111111111111111111

7
répondu Xymostech 2013-03-28 15:14:25

sur le paquet Linux glibc-headers fournit un en-tête #include <ieee754.h> avec des définitions de types à virgule flottante, par exemple:

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
répondu Maxim Egorushkin 2015-09-17 22:01:18
  1. ne faites pas de fonctions qui font plusieurs choses.
  2. ne pas masquer puis déplacer; déplacer puis masquer.
  3. ne modifiez pas les valeurs inutilement parce que c'est lent, destructeur de cache et sujet aux erreurs.
  4. N'utilisez pas de nombres magiques.
/* 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
répondu Barry 2018-09-22 22:15:43

lancez un pointeur vers la variable floating point comme quelque chose comme un unsigned int. Ensuite, vous pouvez déplacer et masquer les bits pour obtenir chaque composant.

float foo;
unsigned int ival, mantissa, exponent, sign;

foo = -21.4f;
ival = *((unsigned int *)&foo);
mantissa = ( ival & 0x7FFFFF);
ival = ival >> 23;
exponent = ( ival  & 0xFF );
ival = ival >> 8;
sign = ( ival & 0x01 );

évidemment, vous n'utiliserez probablement pas les ints non signés pour les bits exposant et sign, mais cela devrait au moins vous donner l'idée.

-1
répondu Gavin H 2013-03-28 15:11:27