17

给定代表 IEEE 754 浮点数的 32 位,如何将数字转换为整数,在表示上使用整数或位操作(而不是使用机器指令或编译器操作进行转换)?

我有以下功能,但在某些情况下会失败:

输入:int x(包含 IEEE 754 格式的 32 位单精度数)

  if(x == 0) return x;

  unsigned int signBit = 0;
  unsigned int absX = (unsigned int)x;
  if (x < 0)
  {
      signBit = 0x80000000u;
      absX = (unsigned int)-x;
  }

  unsigned int exponent = 158;
  while ((absX & 0x80000000) == 0)
  {
      exponent--;
      absX <<= 1;
  }

  unsigned int mantissa = absX >> 8;

  unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff);
  printf("\nfor x: %x, result: %x",x,result);
  return result;
4

7 回答 7

23

C 有“联合”来处理这种类型的数据视图:

typedef union {
  int i;
  float f;
 } u;
 u u1;
 u1.f = 45.6789;
 /* now u1.i refers to the int version of the float */
 printf("%d",u1.i);
于 2013-11-22T18:23:25.620 回答
16

&x给出 x 的地址,所以有float*类型。

(int*)&x将该指针转换为指向事物的int指针int*

*(int*)&x将该指针取消引用为一个int值。在不同尺寸的机器上int,它不会做你相信的事情。float

并且可能存在字节顺序问题。

该解决方案用于快速反平方根算法。

于 2012-09-09T21:01:27.780 回答
7

(有人应该仔细检查这个答案,特别是边界情况和负值的四舍五入。另外,我写它是为了四舍五入。为了重现 C 的转换,这应该改为向零舍入。)

本质上,该过程是:

将 32 位分为 1 个符号位 ( s )、8 个指数位 ( e ) 和 23 个有效位 ( f )。我们将它们视为二进制补码整数。

如果e为 255,则浮点对象要么是无穷大(如果f为零)要么是 NaN(否则)。这种情况下不能进行转换,应该报错。

否则,如果e不为零,则将 2 24添加到f。(如果e不为零,则有效数在其前面隐含 1 位。添加 2 24使该位在f中显式。)

从e中减去 127 。(这会将指数从其偏差/编码形式转换为实际指数。如果我们要对任何值进行一般转换,我们将不得不处理e为零时的特殊情况:减去 126 而不是 127。但是,因为我们只是转换为整数结果,我们可以忽略这种情况,只要这些微小输入数字的整数结果为零。)

如果s为 0(符号为正)且e为 31 或更大,则该值溢出有符号 32 位整数(它为 2 31或更大)。无法进行转换,应该报错。

如果s为 1(符号为负)且e大于 31,则该值溢出有符号 32 位整数(小于或等于 -2 32)。如果s为 1,e为 32,并且f大于 2 24(设置了任何原始有效位),则该值溢出有符号 32 位整数(小于 -2 31;如果原始f为零,它将恰好是 -2 31,不会溢出)。以上任何一种情况都无法进行转换,应该报错。

现在我们有一个s、一个e和一个f来表示一个不会溢出的值,所以我们可以准备最终值。

如果s为 1,则将f设置为-f

指数值适用于 1(包括)和 2(不包括)之间的有效位,但我们的有效位从 2 24位开始。所以我们必须对此进行调整。如果e是 24,我们的有效位是正确的,我们就完成了,所以返回f作为结果。如果e大于 24 或小于 24,我们必须适当地移动有效数。此外,如果我们要将f向右移动,我们可能必须对其进行四舍五入,以将结果四舍五入为最接近的整数。

如果e大于 24,则将f左移e -24 位。返回f作为结果。

如果e小于 -1,则浮点数在 -½ 和 ½ 之间,互斥。返回 0 作为结果。

否则,我们将f右移 24 -e位。但是,我们将首先保存舍入所需的位。将r设置为将 f 转换为无符号 32 位整数并将其左移 32-(24- e ) 位(等效地,左移 8+ e位)的结果。这需要将移出f的位(如下)并在 32 位中“向左调整”它们,因此我们有一个固定的起始位置。

将f右移24 -e位。

如果r小于 2 31,则什么也不做(这是向下舍入;移位截断位)。如果r大于 2 31,则将f加一(这是向上取整)。如果r等于 2 31 ,则将f的低位添加到f。(如果f是奇数,则将f加一。在两个相等接近的值中,这将舍入为偶数。)返回f

于 2012-09-09T23:49:34.390 回答
6
// With the proviso that your compiler implementation uses
// the same number of bytes for an int as for a float:
// example float
float f = 1.234f;
// get address of float, cast as pointer to int, reference
int i = *((int *)&f);
// get address of int, cast as pointer to float, reference
float g = *((float *)&i);
printf("%f %f %08x\n",f,g,i);
于 2018-11-23T14:00:10.660 回答
5
float x = 43.133;
int y;

assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...
于 2012-09-09T21:28:52.743 回答
1

您可以使用参考来投射浮点数。像这样的演员不应该生成任何代码。

C++

float f = 1.0f;
int i = (int &)f;
printf("Float %f is 0x%08x\n", f, i);

输出:

Float 1.000000 is 0x3f800000

如果你想要 c++ 风格的演员,请使用 reinterpret_cast,就像这样。

int i = reinterpret_cast<int &>(f);

它不适用于表达式,您必须将其存储在变量中。

    int i_times_two;
    float f_times_two = f * 2.0f;
    i_times_two = (int &)f_times_two;

    i_times_two = (int &)(f * 2.0f);
main.cpp:25:13: error: C-style cast from rvalue to reference type 'int &'
于 2015-08-11T20:41:24.550 回答
0

您不能(有意义地)以这种方式将浮点数转换为“整数”(signed intint)。

它可能最终具有整数类型,但它实际上只是 IEEE754 编码空间的索引,本身并不是一个有意义的值。

您可能会争辩说unsignedint 作为位模式和整数值具有双重用途,但int事实并非如此。


有符号整数的位操作也存在平台问题。

于 2012-09-09T21:00:03.220 回答