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,
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;
}
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
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()
.
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.
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
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. */
/* 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. */