获取unsigned int或float(C)的尾数(浮点数)

时间:2018-04-12 18:59:38

标签: c ieee-754 mantissa

所以,我试图编写一个函数,以其(尾数* 2 ^指数)格式打印给定的浮点数(n)。我能够得到符号和指数,但不能得到尾数(无论数字是多少,尾数总是等于0.000000)。我所拥有的是:

unsigned int num = *(unsigned*)&n;
unsigned int m = num & 0x007fffff;
mantissa = *(float*)&m;

关于问题可能是什么的任何想法?

3 个答案:

答案 0 :(得分:1)

C库包含执行此确切任务的函数frexp

int expon;
float mant = frexpf(n, &expon);
printf("%g = %g * 2^%d\n", n, mant, expon);

另一种方法是使用log2fexp2f

if (n == 0) {
  mant  = 0;
  expon = 0;
} else {
  expon = floorf(log2f(fabsf(n)));
  mant = n * exp2f(-expon);
}

这两种技术可能会为同一输入提供不同的结果。例如,在我的计算机上,frexpf技术将4描述为0.5×2 3 ,但log2f技术将4描述为1×2 2 。从数学上讲,两者都是正确的。此外,frexp会为您提供尾数的确切位,而log2fexp2f可能会对最后一位或两位进行舍入。

您应该知道*(unsigned *)&n*(float *)&m违反了针对“类型惩罚”的规则并且具有未定义的行为。如果要获取具有与float相同的位表示形式的整数,反之亦然,请使用union:

union { uint32_t i; float f; } u;
u.f = n;
num = u.i;

(注意:自从大约2003年以来,这种工会的使用在C中得到了明确的定义,但是,由于C ++委员会长期以来没有充分注意进入C的变化的习惯,因此它的正式定义不明确。 C ++。)

您还应该知道IEEE浮点数使用“有偏差”的指数。初始化float变量的尾数字段但将其指数字段保留为零时,可以使用大负值指数表示数字:换句话说,数字如此之小以至于printf("%f", n)会将其打印为零。每当printf("%f", variable)打印为零时,请将%f更改为%g%a,然后在假设variable实际为零之前重新运行该程序。

答案 1 :(得分:0)

除了zwol的评论之外:如果你想自己做,你必须掌握一些关于IEEE-754浮标内部的知识。完成后,您可以编写类似

的内容
#include <stdlib.h>
#include <stdio.h>
#include <math.h>               // for testing only

typedef union {
  float value;
  unsigned int bits; // assuming 32 bit large ints (better: uint32_t)
} ieee_754_float;



// clang -g3 -O3 -W -Wall -Wextra -Wpedantic -Weverything -std=c11 -o testthewest testthewest.c -lm
int main(int argc, char **argv)
{

  unsigned int m, num;
  int exp; // the exponent can be negative
  float n, mantissa;
  ieee_754_float uf;

  // neither checks nor balances included!

  if (argc == 2) {
    n = atof(argv[1]);
  } else {
    exit(EXIT_FAILURE);
  }

  uf.value = n;
  num = uf.bits;
  m = num & 0x807fffff;         // extract mantissa (i.e.: get rid of sign bit and exponent)
  num = num & 0x7fffffff;       // full number without sign bit
  exp = (num >> 23) - 126;      // extract exponent and subtract bias
  m |= 0x3f000000;              // normalize mantissa (add bias)
  uf.bits = m;
  mantissa = uf.value;
  printf("n = %g, mantissa = %g, exp = %d, check %g\n", n, mantissa, exp, mantissa * powf(2, exp));

  exit(EXIT_SUCCESS);
}

注意:上面的代码是快速和肮脏(tm)物种之一,不适合生产。它也缺乏对次正规(非正规)数的处理,这是必须包含的东西。提示:将尾数乘以2的大幂(例如:2 ^ 25或在​​该球场中)并相应地调整指数(如果你取我的例子的值减去25)。

答案 2 :(得分:0)

你正在剥离指数的位,留下0.指数0是特殊的,它意味着数字是非规范化的并且非常小,位于可表示数字范围的最底部。我想你会发现,如果仔细观察,你的结果并不完全是零,只是这么小,以至于你无法区分它们。

要为尾数得到一个合理的数字,你需要重新放入一个合适的指数。如果你想要一个1.0到2.0范围内的尾数,你需要一个0的指数,但添加偏差意味着你真的需要127的指数。

unsigned int m = (num & 0x007fffff) | (127 << 23);
mantissa = *(float*)&m;

如果你想要一个完全整数的尾数,你需要一个23的指数,它的偏差就是150.

unsigned int m = (num & 0x007fffff) | ((23+127) << 23);
mantissa = *(float*)&m;