将float转换为int(大整数)的函数

时间:2014-09-06 11:06:12

标签: c bit-manipulation

这是一个大学问题。只是为了确保:-)我们需要实现(浮动)x

我有以下代码必须将整数x转换为存储在无符号整数中的浮点二进制表示。

unsigned float_i2f(int x) {
  if (!x) return x;

  /* get sign of x */
  int sign = (x>>31) & 0x1;

  /* absolute value of x */
  int a = sign ? ~x + 1 : x;

  /* calculate exponent */
  int e = 0;
  int t = a;
  while(t != 1) {
    /* divide by two until t is 0*/
    t >>= 1;
    e++;
  };

  /* calculate mantissa */
  int m = a << (32 - e);
  /* logical right shift */
  m = (m >> 9) & ~(((0x1 << 31) >> 9 << 1));

  /* add bias for 32bit float */
  e += 127;

  int res = sign << 31;
  res |= (e << 23);
  res |= m;

  /* lots of printf */

  return res;
}

我现在遇到的一个问题是,当我的整数太大时,我的代码就会失败。我实施了这个控制程序:

float f = (float)x;
unsigned int r;
memcpy(&r, &f, sizeof(unsigned int));

这当然总会产生正确的输出。

现在当我做一些测试运行时,这是我的输出(GOAL是它需要的,结果是我得到的)

:!make && ./btest -f float_i2f -1 0x80004999                                                                  
make: Nothing to be done for `all'.
Score   Rating  Errors  Function
x: [-2147464807]        10000000000000000100100110011001
sign: 1
expone: 01001110100000000000000000000000
mantis: 00000000011111111111111101101100
result: 11001110111111111111111101101100
GOAL:   11001110111111111111111101101101

所以在这种情况下,添加1作为LSB。

下一个案例:

:!make && ./btest -f float_i2f -1 0x80000001
make: Nothing to be done for `all'.
Score   Rating  Errors  Function
x: [-2147483647]        10000000000000000000000000000001
sign: 1
expone: 01001110100000000000000000000000
mantis: 00000000011111111111111111111111
result: 11001110111111111111111111111111
GOAL:   11001111000000000000000000000000

这里1加到指数中,而尾数是它的补码。

我试着在互联网上查看ip以及我的书籍等等,但是我找不到任何关于这个问题的引用。我猜这与尾数只有23位的事实有关。但是我该如何处理呢?

编辑:本部分已经过了以下的评论。 int l必须是无符号的。

int x = 2147483647; 
float f = (float)x;

int l = f;
printf("l: %d\n", l);

然后我变成-2147483648。

这怎么可能发生?所以C正在做错投射?

希望有人可以帮助我! 谢谢 马库斯

编辑2:

我的更新代码现在是:

unsigned float_i2f(int x) {
  if (x == 0) return 0;
  /* get sign of x */
  int sign = (x>>31) & 0x1;

  /* absolute value of x */
  int a = sign ? ~x + 1 : x;

  /* calculate exponent */
  int e = 158;
  int t = a;
  while (!(t >> 31) & 0x1) {
    t <<= 1;
    e--;
  };

  /* calculate mantissa */
  int m = (t >> 8) & ~(((0x1 << 31) >> 8 << 1));
  m &= 0x7fffff;

  int res = sign << 31;
  res |= (e << 23);
  res |= m;

  return res;
}

我还发现该代码适用于-2 ^ 24,2 ^ 24范围内的所有整数。上面/下面的所有东西有时都有效,但大多数都没有。

缺少一些东西,但我真的不知道是什么。任何人都可以帮助我吗?

1 个答案:

答案 0 :(得分:1)

打印的答案绝对正确,因为它完全取决于正在投射的数字的基本表示。但是,如果我们理解数字的二进制表示,您就不会对此结果感到惊讶。

要理解隐式转换与赋值运算符相关(参考C99标准6.5.16)。 C99标准继续说:

  

6.3.1.4实际浮动和整数   当实数浮动类型的有限值被转换为除_Bool之外的整数类型时,小数部分被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为是未定义的。

您之前的示例说明了由于分配了目标类型范围之外的值而导致的未定义行为。尝试将负值分配给无符号类型,而不是将浮点数转换为整数。

以下代码段中的断言应该防止发生任何未定义的行为。

#include <limits.h>
#include <math.h>
unsigned int convertFloatingPoint(double v) {
   double d;
   assert(isfinite(v));
   d = trunc(v);
   assert((d>=0.0) && (d<=(double)UINT_MAX));
   return (unsigned int)d;
}

另一种做同样事情的方法,创建一个包含32位整数和浮点数的联合。 int和float现在只是查看同一位内存的不同方式;

union {
    int    myInt;
    float myFloat;
} my_union;

my_union.myInt = 0x BFFFF2E5;

printf("float is &#37;f\n", my_union.myFloat);

float is -1.999600

你告诉编译器取你拥有的数字(大整数)并将它变成浮点数,而不是解释数字AS浮点数。为此,您需要告诉编译器以不同的形式从该地址读取数字,所以:

myFloat = *(float *)&myInt ;

这意味着,如果我们把它拆开,从右边开始:

  • &amp; myInt - 内存中保存整数的位置。
  • (float *) - 实际上,我希望编译器使用它作为float的指针,而不是编译器认为的那样。
  • * -从右边的地址读取。
  • myFloat = - 将此变量设置为右侧的任何内容。

所以,你告诉编译器:在(myInt)的位置,有一个浮点数,现在将浮点数放入myFloat。