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

Posted

技术标签:

【中文标题】C# \'unsafe\' 函数 - *(float*)(&result) 与 (float)(result)【英文标题】:C# 'unsafe' function — *(float*)(&result) vs. (float)(result)C# 'unsafe' 函数 - *(float*)(&result) 与 (float)(result) 【发布时间】:2012-09-25 05:28:12 【问题描述】:

谁能简单解释一下下面的代码:

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

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

注意:以上代码使用了unsafe函数

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

return (float)(result);

如果你返回*(float*)(&amp;result),是否需要使用unsafe函数?

【问题讨论】:

我在 mscorlib/System/BitConvert/ToSingle 中找到了使用 ILSpy 的示例代码。没有给出任何解释。我需要了解流程是什么,因为我需要将其转换为 php 重新区分return (float)result; - 这是一个转换 - 它将整数123 转换为浮点123.0F - 但那是不是 重新诠释的演员阵容; 123123.0F 的字节完全不同re-interpretive 演员只是说“这里有 4 个字节;现在将它们视为 float 以这种方式使用指针重新解释数据称为“类型双关”。 我很好奇这是否是将 4 个字节转换为浮点数的唯一方法。我知道它在 C 中,但指针的东西在那里也更常见 @gchimuel 要在 PHP 中执行此操作,请参阅***.com/questions/2624869/bytes-convert-to-float-php,而不是尝试对一种完全不同的语言进行逆向工程。如果您已经有一个字节数组,只需使用 unpack。 【参考方案1】:

因为 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;

【讨论】:

太棒了!另外,我在一本书中听说过这个东西(CLR via C#),但没有意识到它可以这样使用。【参考方案2】:

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

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

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

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

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


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

假设以下代码:

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

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

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

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

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

【讨论】:

“最后一个零,那种“破坏位模式”的分数可能是为了使浮点数具有精确的十进制表示?本身并不准确 - 想想我们如何以十进制将 2/3 舍入为 0.66667 而不是 0.66666 +!因为使用了“险恶”这个词 @Random832:你说得对,2.4 不能用 32 位浮点数精确表示(我一开始没有意识到),但存在的数字在小数点中都不是无限小数和二进制编号系统。例如。 11/4 = 2.75 十进制 = 10.11 二进制。 @MartinLiversage 我只是在解释为什么它使用 ..999A 而不是 ..9999 - 与十进制 2/3 末尾的 7 而不是 6 的原因相同。 @Random832:对不起,误解了,但这个例子看起来太做作了,我推测有人通过使用位模式制作了一个随机浮点数,然后调整了最后一位数字以使其成为可能往返浮点字面量。【参考方案3】:

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

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

public static float sample()

    int result = 154 + (153 << 8) + (25 << 16) + (64 << 24);
    return BitConverter.ToSingle(BitConverter.GetBytes(result), 0);

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

【讨论】:

你的答案和布拉德利·史密斯的完全一样。【参考方案4】:

如果我正确地解释了该方法的作用,这是一个安全的等价物:

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

【讨论】:

智能转换!我想知道安全版本的性能与不安全版本相比如何。 @RuneGrimstad 可能不是很好,考虑到它正在复制数据而不是仅仅转换为不同类型的指针。尽管如此,这可能是不使用不安全代码的最简单方法。 同意。如果还可以证明直接进行浮点计算会更快。仍然是一个非常好的解决方案! 很遗憾需要一个中间的byte[]。有一个BitConverter.Int64BitsToDouble() 方法与longdouble 做同样的事情,但没有'Int32BitsToSingle()' 方法...... 您也可以使用“联合”“安全地”执行此操作,即使用StructLayout(LayoutKind.Explicit)【参考方案5】:

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

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

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

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

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

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

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

【讨论】:

【参考方案6】:

Re : 它在做什么?

它获取存储在 int 中的字节的值,并将这些字节解释为浮点数(不进行转换)。

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

【讨论】:

以上是关于C# 'unsafe' 函数 - *(float*)(&result) 与 (float)(result)的主要内容,如果未能解决你的问题,请参考以下文章

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

C# 错误CS0227 不安全代码只会在使用 /unsafe 编译的情况下出现

将值写入字节数组而不使用 C# unsafe 或 fixed 关键字

C# IntPtr类型和使用/unsafe选项编译程序

unsafe,fixed与GCHandle

无法加载文件或程序集 System.Runtime.CompilerServices.Unsafe