C#'unsafe'函数 - *(float *)(& result)vs.(float)(result)

时间:2012-10-04 08:04:10

标签: c# unsafe type-punning

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

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)

,是否有必要使用不安全的功能

6 个答案:

答案 0 :(得分:75)

在.NET上,使用32位存储的IEEE binary32单精度浮点数表示float。显然,代码通过将位组合成int然后使用float将其转换为unsafe来构造此数字。演员用C ++术语称为reinterpret_cast,在执行强制转换时不进行转换 - 这些位只是重新解释为新类型。

IEEE single precision floating number

汇总的数字是十六进制的4019999A或二进制的01000000 00011001 10011001 10011010

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

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

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


那么常规演员和这个奇怪的“不安全演员”之间的区别是什么?

假设以下代码:

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

第一个转换采用整数1075419546并将其转换为浮点表示,例如1075419546f。这涉及计算将原始整数表示为浮点数所需的符号,指数和分数位。这是一项必须完成的非平凡计算。

第二次演员阵容更加险恶(并且只能在不安全的环境中演出)。 &result获取result的地址,返回指向存储整数1075419546的位置的指针。然后可以使用指针解除引用运算符*来检索指针指向的值。使用*&result将检索存储在该位置的整数,但是首先将指针强制转换为float*(指向float的指针),而是从内存位置检索浮点数,从而导致float 2.4f已分配给unsafeCast。所以*(float*) &result的叙述是给我一个指向result的指针,并假设指针是指向float的指针,并检索指针指向的值

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

一般来说,执行类似的转换会在很多方面失败,但是使用unsafe,你告诉编译器你知道你在做什么。

答案 1 :(得分:18)

如果我正在解释方法正确的做法,这是一个安全的等价物:

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

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

如前所述,它正在将int值重新解释为float

答案 2 :(得分:3)

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

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

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

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

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

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

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

答案 3 :(得分:2)

回复:它在做什么?

它取存储int的字节值,而不是将这些字节解释为float(不进行转换)。

幸运的是,浮点数和整数具有相同的data size 4个字节。

答案 4 :(得分: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;
}

答案 5 :(得分:0)

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

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

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

但是它不会再快了,你也可以使用浮点数/双打和数学函数。