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

gch*_*uel 44 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
}
Run Code Online (Sandbox Code Playgroud)

注意:上面的代码使用不安全的功能

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

return (float)(result);
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*age 75

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

IEEE单精度浮点数

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

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

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

最后的零那种"打破分数的位模式"可能是为了使浮点数匹配可以使用浮点文字写入的东西?


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

假设以下代码:

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

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

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

与第一次投射相反,第二次投射不需要任何计算.它只是将存储的32位推resultunsafeCast(幸运的是也是32位).

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


Bra*_*ith 18

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

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

   byte[] data = BitConverter.GetBytes(result);
   return BitConverter.ToSingle(data, 0);
}
Run Code Online (Sandbox Code Playgroud)

正如已经说过的那样,它正在重新解释这个int价值float.

  • 智能转换!我想知道如何将安全版本的性能与不安全版本进行比较. (2认同)
  • @RuneGrimstad可能不那么好,考虑到它是复制数据而不是仅仅转换为不同类型的指针.尽管如此,它可能是最简单的方法,不使用不安全的代码. (2认同)
  • 遗憾的是需要一个中间的`byte []`.有一个`BitConverter.Int64BitsToDouble()`方法用`long`和`double`做同样的事情,但没有'Int32BitsToingle()'方法...... (2认同)