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*)(&result)
,是否需要使用unsafe函数?
【问题讨论】:
我在 mscorlib/System/BitConvert/ToSingle 中找到了使用 ILSpy 的示例代码。没有给出任何解释。我需要了解流程是什么,因为我需要将其转换为 php。 重新区分return (float)result;
- 这是一个转换 - 它将整数123
转换为浮点123.0F
- 但那是不是 重新诠释的演员阵容; 123
和 123.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
:
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
。这涉及计算将原始整数表示为浮点数所需的符号、指数和小数位。这是一个必须完成的重要计算。
第二个演员更险恶(只能在不安全的情况下执行)。 &result
获取result
的地址,返回一个指向整数1075419546
存储位置的指针。然后可以使用指针取消引用运算符*
来检索指针指向的值。使用*&result
将检索存储在该位置的整数,但是首先将指针转换为float*
(指向float
的指针),而是从内存位置检索浮点数,从而分配浮点数2.4f
到unsafeCast
。所以*(float*) &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()
方法与long
和double
做同样的事情,但没有'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 编译的情况下出现