45

任何人都可以用简单的方式解释下面的代码:

public unsafe static float sample(){    
      int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

      return *(float*)(&result); //don't know what for... please explain
}

注意:以上代码使用了不安全的函数

对于上面的代码,我很难过,因为我不明白它的返回值与下面的返回值有什么区别:

return (float)(result);

如果您返回,是否有必要使用不安全的功能*(float*)(&result)

4

6 回答 6

76

在 .NET 上,a使用 32 位存储floatIEEE binary32单精度浮点数表示。显然,代码通过将位组装成 anint然后将其转换为floatusing 来构造这个数字unsafe。转换在 C++ 术语中称为 a reinterpret_cast,在执行转换时不进行任何转换——这些位只是被重新解释为一种新类型。

IEEE 单精度浮点数

组装的数字是4019999A十六进制或01000000 00011001 10011001 10011010二进制:

  • 符号位为 0(它是一个正数)。
  • 指数位是10000000(或 128)导致指数 128 - 127 = 1(分数乘以 2^1 = 2)。
  • 如果没有别的,小数位00110011001100110011010几乎具有可识别的零和一模式。

返回的 float 具有与 2.4 转换为浮点的完全相同的位,整个函数可以简单地替换为 literal 2.4f

分数的“破坏位模式”的最后一个零可能是为了使浮点匹配可以使用浮点文字编写的东西?


那么常规演员表和这种奇怪的“不安全演员表”有什么区别?

假设以下代码:

int result = 0x4019999A // 1075419546
float normalCast = (float) result;
float unsafeCast = *(float*) &result; // Only possible in an unsafe context

第一次转换采用整数1075419546并将其转换为浮点表示,例如1075419546f. 这涉及计算将原始整数表示为浮点数所需的符号、指数和小数位。这是一个必须完成的重要计算。

第二个演员更险恶(只能在不安全的情况下执行)。&result获取返回指向整数存储result位置的指针的地址。然后可以使用1075419546指针解引用运算符来检索指针指向的值。*Using*&result将检索存储在该位置的整数,但是首先将指针转换为 a float*(指向 a 的指针float),而是从内存位置检索浮点数,从而将浮点数2.4f分配给unsafeCast. 所以叙述*(float*) &result给我一个指针result并假设指针是指向 a 的指针float并检索指针指向的值

与第一次演员相比,第二次演员不需要任何计算。它只是将存储的 32 位推resultunsafeCast(幸运的是也是 32 位)。

一般来说,执行这样的强制转换可能会在很多方面失败,但通过使用unsafe你告诉编译器你知道你在做什么。

于 2012-10-04T08:09:14.920 回答
19

如果我正在解释该方法正确执行的操作,则这是一个安全的等价物:

public static float sample() {    
   int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);

   byte[] data = BitConverter.GetBytes(result);
   return BitConverter.ToSingle(data, 0);
}

正如已经说过的,它将值重新解释intfloat.

于 2012-10-04T08:11:58.367 回答
3

这看起来像是一次优化尝试。不是进行浮点计算,而是对浮点数的整数表示进行整数计算。

请记住,浮点数像整数一样存储为二进制值。

计算完成后,您将使用指针和强制转换将整数转换为浮点值。

这与将值转换为浮点数不同。这会将 int 值 1 转换为 float 1.0。在这种情况下,您将 int 值转换为存储在 int 中的二进制值所描述的浮点数。

很难正确解释。我会找一个例子。:-)

编辑:看这里:http ://en.wikipedia.org/wiki/Fast_inverse_square_root

您的代码基本上与本文中描述的相同。

于 2012-10-04T08:11:01.970 回答
2

Re: 它在做什么?

它采用存储 int 的字节的值,而不是将这些字节解释为浮点数(不进行转换)。

幸运的是,浮点数和整数具有相同的 4 字节数据大小

于 2012-10-04T08:10:28.570 回答
2

因为 Sarge Borsch 问过,这里是“Union”等价物:

[StructLayout(LayoutKind.Explicit)]
struct ByteFloatUnion {
  [FieldOffset(0)] internal byte byte0;
  [FieldOffset(1)] internal byte byte1;
  [FieldOffset(2)] internal byte byte2;
  [FieldOffset(3)] internal byte byte3;
  [FieldOffset(0)] internal float single;
}

public static float sample() {
   ByteFloatUnion result;
   result.single = 0f;
   result.byte0 = 154;
   result.byte1 = 153;
   result.byte2 = 25;
   result.byte3 = 64;

   return result.single;
}
于 2013-09-24T16:33:36.377 回答
0

正如其他人已经描述的那样,它将 int 的字节视为浮点数。

如果不使用这样的不安全代码,您可能会得到相同的结果:

public static float sample()
{
    int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);
    return BitConverter.ToSingle(BitConverter.GetBytes(result), 0);
}

但是它不会再快了,你不妨使用浮点数/双精度数和数学函数。

于 2012-10-04T08:21:48.587 回答