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

Can someone explain the following codes in a simple way:

public unsafe static float sample(){ int result = 154 + (153 << 8) + (25 << 16) + (64 << 24); return *(float*)(&result); //don't know what for... please explain } 

Note: the above code uses an unsafe function

I have a hard time for the above code because I don’t understand what the difference between its return value is compared with the return value below:

 return (float)(result); 

Do I need to use an unsafe function if you return *(float*)(&result) ?

+44
c # type-punning unsafe
04 Oct
source share
6 answers

In .NET, a float is represented using IEEE binary32 as a single-precision floating-point number stored using 32 bits. Apparently, the code builds this number by collecting bits in an int , and then translates it into a float using unsafe . Listing is what reinterpret_cast is called in terms of C ++, where the conversion is not performed when the translation is performed - the bits are simply reinterpreted as a new type.

IEEE single precision floating number

The collected number 4019999A in hexadecimal or 01000000 00011001 10011001 10011010 in binary format:

  • The sign bit is 0 (this is a positive number).
  • The exponent bits are 10000000 (or 128), which results in an exponent of 128 - 127 = 1 (the fraction is multiplied by 2 ^ 1 = 2).
  • Fractional digits 00110011001100110011010 , which, if nothing else, have almost no recognizable pattern of zeros and ones.

The returned float has the same bits as 2.4, converted to a floating point, and the whole function can simply be replaced with the 2.4f literal.

The last zero that “breaks the bit pattern” of the fraction can possibly make the float match what could be written using a floating point literal?




So what is the difference between regular cast and this strange "unsafe act"?

Assume the following code:

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

The first actor takes an integer 1075419546 and converts it to his floating point representation, for example. 1075419546f . This includes calculating the sign bits, exponent, and fractions needed to represent the original integer as a floating point number. This is a nontrivial calculation that needs to be done.

The second cast is more sinister (and can only be done in an unsafe context). &result takes the address of result , returning a pointer to the place where the integer 1075419546 . The pointer dereference operator * can then be used to retrieve the value that the pointer points to. Using *&result will retrieve the integer stored in the location, but by first hovering over the float* (pointer to float ), the float from the memory cell is retrieved instead, which results in float 2.4f to unsafeCast . So the narrative *(float*) &result gives me a pointer to result and assumes the pointer is a pointer to a float and retrieves the value that the pointer points to.

Unlike the first throw, the second throw does not require any calculations. It just drags the 32 bits stored in result to unsafeCast (fortunately also 32 bits).

In general, executing this type can fail in many ways, but using unsafe , you tell the compiler that you know what you are doing.

+75
04 Oct
source share

If I interpret that the method does the right thing, this is the safe equivalent:

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

As already mentioned, it re-interprets the int value as a float .

+18
Oct 04 '12 at 8:11
source share

It looks like an optimization attempt. Instead of floating point calculations, you do integer calculations in the Integer view of the floating point number.

Remember that float is stored as binary values, like int.

After performing the calculation, you use pointers and casting to convert the integer to the float value.

This is not the same as distinguishing a value from a float. This will turn the value of int 1 to float 1.0. In this case, you turn the int value into a floating point number, described by the binary value stored in int.

It is hard to explain correctly. I will look for an example. :-)

Edit: See here: http://en.wikipedia.org/wiki/Fast_inverse_square_root

Your code basically does the same as described in this article.

+3
04 Oct '12 at 8:11
source share

Re: What is he doing?

It takes the value of the bytes stored in int, and instead interprets these bytes as a float (without conversion).

Fortunately, float and ints have the same data size of 4 bytes.

+2
04 Oct '12 at 8:10
source share

Because Sarge Borsh asked, here is the equivalent of “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; } 
+2
Sep 24 '13 at 16:33
source share

As others have already described, it treats int bytes as if they were a float.

You can get the same result without using unsafe code, for example:

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

But then it will not be very fast, and you can also use float / doubles and mathematical functions.

0
Oct 04 '12 at 8:21
source share



All Articles