2012-10-04 86 views
44

任何人都可以用简单的方式下面的代码解释:C# '不安全' 的功能 - *(浮动*)(结果)与(浮动)(结果)

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),是否需要使用不安全的功能?

+0

你在哪里找到的样本?它没有任何关于它要返回的内容的解释吗? –

+0

这是错的..'result'不是一个指针..我不能将它的地址转换为浮点指针我猜 – Anirudha

+1

我找到使用mscorlib/System/BitConvert/ToSingle中的ILSpy的示例代码。没有给出解释。我需要了解什么是流程,因为我需要将其转换为PHP。 – gchimuel

回答

75

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

IEEE single precision floating number

组装的数量是十六进制4019999A01000000 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。这包括计算将原始整数表示为浮点数所需的符号,指数和小数位。这是一个不平凡的计算,必须完成。

第二次演员更险恶(只能在不安全的情况下执行)。 &result将地址result返回指向存储整数1075419546的位置的指针。指针解引用运算符*然后可以用于检索指针指向的值。使用*&result将检索存储在该位置的整数,但是通过首先将指针指向float*(指向float的指针),则会从存储器位置检索浮点数,从而导致float 2.4f被指定为unsafeCast。所以*(float*) &result的叙述是给了我一个指向result的指针,并且假设指针是指向float的指针并检索指针指向的值。

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

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

+0

“那种”打破了位图模式“的最后零点可能是为了让浮点数具有精确的十进制表示?”不是确切的本身 - 想想如何在小数我们轮2/3到0.66667不是0.66666 – Random832

+1

+!使用单词“sinister” – Nahum

+0

@ Random832:2.4是无法用32位浮点数(我最初没有意识到)精确表示的,但是存在的数字不是无穷小数和二进制编号系统。例如。 11/4 = 2.75十进制= 10.11二进制。 –

2

回复:它在做什么?

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

幸运的是,浮动和整数有4个字节的data size

3

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

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

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

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

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

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

您的代码,基本上是做一样的这篇文章中所述。

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

智能转换!我想知道安全版本的性能如何与不安全的版本进行比较。 –

+2

@RuneGrimstad可能不是太好,考虑到它是复制数据而不是仅仅投射到不同类型的指针。不过,这可能是最简单的方法,而不使用不安全的代码。 –

+0

同意。如果可能会发现,直接进行浮点计算会更快。仍然是一个很好的解决方案 –

0

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

你可能会得到相同的结果,而无需使用类似这样不安全的代码:

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

但后来它不会是非常快的更多,你还不如用花车/双打和数学函数。

+2

你的答案与Bradley Smith的完全一样。 – Guillaume

2

由于Sarge的罗宋汤问,这里的“联盟”相当于:

[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; 
} 
+0

太棒了!另外,我在一本书(CLR通过C#)中听说过这件事,但没有意识到它可以像这样使用。 –