如何获取浮点数的符号,尾数和指数

时间:2013-03-28 15:00:54

标签: c floating-point emulation

我有一个程序,它运行在两个处理器上,其中一个处理器没有浮点支持。所以,我需要在该处理器中使用固定点执行浮点计算。为此,我将使用浮点仿真库。

我需要首先在处理器上提取浮点数的符号,尾数和指数,它们支持浮点数。所以,我的问题是如何获得单个精度浮点数的符号,尾数和指数。

按照此图中的格式

enter image description here 这就是我到目前为止所做的,但除了符号,尾数和指数都不正确。我想,我错过了什么。

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

8 个答案:

答案 0 :(得分:20)

我认为最好使用工会进行演员表演,这一点更清楚。

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

基于http://en.wikipedia.org/wiki/Single_precision

的示例

答案 1 :(得分:19)

找出直接支持浮点的CPU上使用的浮点数的格式,并将其分解为这些部分。最常见的格式是IEEE-754

或者,您可以使用一些特殊功能(double frexp(double value, int *exp);double ldexp(double x, int exp);)获取这些部分,如this answer所示。

Another option%aprintf()一起使用。

答案 2 :(得分:19)

我的建议是坚持使用规则0而不是重做已经做过的标准库,如果这足够的话。查看math.h(标准C ++中的cmath)和函数frexp,frexpf,frexpl,它们在其有效数和指数部分中打破浮点值(double,float或long double)。要从有效数字中提取符号,您可以使用signbit,也可以使用math.h / cmath或copysign(仅限C ++ 11)。一些替代方案,语义不同,是modf和ilogb / scalbn,可在C ++ 11中找到; http://en.cppreference.com/w/cpp/numeric/math/logb比较它们,但我没有在文档中找到所有这些函数如何使用+/- inf和NaNs。最后,如果你真的想使用位掩码(例如,你迫切需要知道确切的位,并且你的程序可能有不同的NaN具有不同的表示,并且你不相信上述函数),至少使所有东西都与平台无关通过使用float.h / cfloat中的宏。

答案 3 :(得分:7)

&错误的位。我想你想要:

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

请记住,当您&时,您正在清除未设置的位。因此,在这种情况下,您希望在得到指数时将符号位清零,并且希望在得到尾数时将符号位和指数置零。

请注意,面具直接来自您的照片。因此,指数掩码将如下所示:

  

0 11111111 00000000000000000000000

并且尾数掩码看起来像:

  

0 00000000 11111111111111111111111

答案 4 :(得分:6)

在Linux包上,glibc-headers提供带有浮点类型定义的头#include <ieee754.h>,例如:

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.  */

答案 5 :(得分:0)

  1. 不要使函数执行多种操作。
  2. 不要掩盖然后转移;转移然后遮罩。
  3. 不要不必要地更改值,因为它速度慢,破坏缓存且容易出错。
  4. 不要使用幻数。
/* 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. */

答案 6 :(得分:0)

这对于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;

如果尝试以二进制/十进制打印它们,则会得到以下信息:

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

答案 7 :(得分:-2)

将指向浮点变量的指针转换为类似unsigned int的指针。然后你可以移动并屏蔽这些位以获得每个组件。

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

显然你可能不会使用无符号整数来表示指数和符号位,但这至少可以给你这个想法。