65

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

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

按照该图的格式,

在此处输入图像描述 这就是我到目前为止所做的,但除了符号,尾数和指数都不正确。我想,我错过了一些东西。

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

7 回答 7

33

我认为最好使用工会来做演员,这更清楚。

#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的示例

于 2013-03-28T15:06:37.663 回答
33

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

于 2013-10-26T16:12:47.350 回答
25

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

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

另一种选择是使用%awith printf()

于 2013-03-28T15:05:58.650 回答
11

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

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

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

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

0 11111111 00000000000000000000000

尾数掩码看起来像:

0 00000000 11111111111111111111111

于 2013-03-28T15:07:09.937 回答
8

在 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.  */
于 2015-09-17T22:01:18.573 回答
2
  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. */
于 2018-09-22T22:15:43.337 回答
1

IEEE_754_types.h有关要提取的联合类型,请参见此标头: floatdoublelong double,(处理字节序)。这是一个摘录:

/*
** - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
**  Single Precision (float)  --  Standard IEEE 754 Floating-point Specification
*/

# define IEEE_754_FLOAT_MANTISSA_BITS (23)
# define IEEE_754_FLOAT_EXPONENT_BITS (8)
# define IEEE_754_FLOAT_SIGN_BITS     (1)

.
.
.

# if (IS_BIG_ENDIAN == 1)
    typedef union {
        float value;
        struct {
            __int8_t   sign     : IEEE_754_FLOAT_SIGN_BITS;
            __int8_t   exponent : IEEE_754_FLOAT_EXPONENT_BITS;
            __uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
        };
    } IEEE_754_float;
# else
    typedef union {
        float value;
        struct {
            __uint32_t mantissa : IEEE_754_FLOAT_MANTISSA_BITS;
            __int8_t   exponent : IEEE_754_FLOAT_EXPONENT_BITS;
            __int8_t   sign     : IEEE_754_FLOAT_SIGN_BITS;
        };
    } IEEE_754_float;
# endif

并查看dtoa_base.c如何将double值转换为字符串形式的演示。

此外,请查看C/CPP 参考书的1.2.1.1.4.2 - Floating-Point Type Memory Layout部分,它非常简单地解释了所有浮点类型的内存表示/布局以及如何解码它们(带插图)遵循实际的 IEEE 754 浮点规范。

它还具有指向非常好的资源的链接,可以更深入地解释。

于 2019-04-29T05:11:23.547 回答