web-dev-qa-db-fra.com

Comment obtenir le signe, la mantisse et l'exposant d'un nombre à virgule flottante

J'ai un programme fonctionnant sur deux processeurs, dont l'un ne prend pas en charge les virgules flottantes. Donc, je dois effectuer des calculs en virgule flottante en utilisant un point fixe dans ce processeur. Pour ce faire, j'utiliserai une bibliothèque d'émulation en virgule flottante. 

Je dois d’abord extraire les signes, les mantisses et les exposants des nombres à virgule flottante sur le processeur qui prennent en charge la virgule flottante. Ma question est donc de savoir comment obtenir le signe, la mantisse et l’exposant d’un nombre à virgule flottante simple précision.

En suivant le format de cette figure, 

enter image description here C'est ce que j'ai fait jusqu'à présent, mais sauf signe, ni mantisse ni exposant ne sont corrects. Je pense qu'il me manque 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
MetallicPriest

Je pense qu'il est préférable d'utiliser les syndicats pour faire le casting, c'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
eran

Découvrez le format des nombres à virgule flottante utilisés sur la CPU qui supporte directement les virgules flottantes et divisez-le en ces parties. Le format le plus courant est IEEE-754 .

Sinon, 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 cette réponse .

Une autre option consiste à utiliser %a avec printf().

18
Alexey Frunze

Mon conseil est de s'en tenir à la règle 0 et de ne pas refaire ce que les bibliothèques standard font déjà, si cela suffit. Regardez math.h (cmath en C++ standard) et les fonctions frexp, frexpf, frexpl, qui cassent une valeur à virgule flottante (double, float ou long double) dans sa partie significande et exposant. Pour extraire le signe de la signification, vous pouvez utiliser Signbit, également dans math.h/cmath ou copysign (uniquement en C++ 11). Certaines alternatives, avec une sémantique différente, 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 NaN. Enfin, si vous voulez vraiment utiliser des masques de bits (par exemple, vous avez désespérément besoin de connaître les bits exacts, et votre programme peut avoir différents NaN avec différentes représentations, et vous ne faites pas confiance aux fonctions ci-dessus), au moins rendez tout indépendant de la plateforme en utilisant les macros dans float.h/cfloat.

15
Pietro Braione

Vous êtes &ing les mauvais bits. Je pense que tu veux:

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

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

Notez que les masques proviennent directement de votre image. Donc, le masque de l'exposant ressemblera à:

0 11111111 00000000000000000000000

et le masque de mantisse ressemblera à:

0 00000000 11111111111111111111111

7
Xymostech

Sur le paquet Linux, glibc-headers fournit en-tête #include <ieee754.h> 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
Maxim Egorushkin
  1. Ne créez pas de fonctions faisant plusieurs choses.
  2. Ne masque pas puis change; décaler puis masquer.
  3. Ne modifiez pas les valeurs inutilement car elles sont lentes, détruisent le cache et sont sujettes 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
Barry