Jump to content
  • Advertisement
Sign in to follow this  
Ned_K

What is going on with: int i = *(int*)&x where x is a float?

This topic is 3931 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, I'm sure many have seen this snippet for inverse square roots: float InvSqrt(float x) { float xhalf = 0.5f*x; int i = *(int*)&x; // get bits for floating value printf("%x\n", i); i = 0x5f3759df - (i>>1); // gives initial guess y0 x = *(float*)&i; // convert bits back to float x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy return x; } What's happening with this line: int i = *(int*)&x; Is that converting float x to a pointer, casting it to an int pointer and then dereferencing it and putting the result in an int? Is that defined? I also see similar code with the *(int*)&var format elsewhere now that I am looking for it.

Share this post


Link to post
Share on other sites
Advertisement
I suppose you could do this if you wanted be able to access the individual bits of a floating point number, since that's would you would have in the integer. I have no idea how valid this sort of thing is, but it certainly looks terribly ugly and unsafe.

Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
I suppose you could do this if you wanted be able to access the individual bits of a floating point number, since that's would you would have in the integer. I have no idea how valid this sort of thing is, but it certainly looks terribly ugly and unsafe.


Write-up on it:

http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf

But he focuses on the constant a few lines down and the overall efficiency gains.

I'm just hung up on the syntax of that one line.

Share this post


Link to post
Share on other sites
It is of course designed for 4 byte integers and single precision floating point values. So it's not going to be covered by the C++ standard.

It looks ugly, but all it does is place the exact same 32 bits that came from a float into what is now interpreted as an integer.

This is so the bitshift and bit manipulation can be performed.

Of course, the constant 0x5f3759df is also dependent on 32 bit integers and IEEE 754.

Share this post


Link to post
Share on other sites
Two little notes:

1) That code can break if you run on a compiler that supports strict aliasing optimizations if they are enabled. Casting through a union is the most common workaround. See this page for more details: http://www.cellperformance.com/mike_acton/2006/06/understanding_strict_aliasing.html

2) The compiler/CPU can do screwy stuff when converting between an integer and float if you set the wrong bits. We had a bug in our endian swapping code a while back where converting an int to a float via address casting caused NaNs in very specific cases. I don't recall the specifics, but it went all the way down to getting the value into a register. I think it was doing something like:

float fData;
Read(&fData, sizeof(4)); // Reads in unswapped data, which involves interpreting the data as an int.
Swap(&fData, sizeof(4)); // Swaps the data to the correct endian

If the unswapped data bits were arranged just right, the value stored in fData was converted to a NaN, basically corrupting the data before the Swap occured.

Basically, we needed to do this instead:

uint32 nData;
Read(&nData, sizeof(4)); // Reads in unswapped data
Swap(&nData, sizeof(4)); // Swaps the data to the correct endian
float fData = UnionCast<float>(nData); // Convert the representation to a float

Moral: Be really careful when converting between types. Even when you know what you are doing, its very easy to introduce either a compiler specific bug or a value specific bug.

Share this post


Link to post
Share on other sites
Quote:
Is that converting float x to a pointer, casting it to an int pointer and then dereferencing it and putting the result in an int?


float pi = 3.14159;
int x = (int)pi;
-> x == 3 (00000003h)

int x = *(int*)π
-> x is a bitwise copy of pi, so you can perform bitwise operations. It most certainly is not 00000003h.

Share this post


Link to post
Share on other sites
Long story short, accessing a memory address through a type-punned pointer (other than char*) gives undefined results according to the standard. In practice, this works as expected on most common platforms, though it will break strict aliasing as pointed out above. "As expected" means that it gives you the bit-pattern of the floating point number, which wouldn't be possible to get through normal means.

Most compilers support an extension to allow you do the same thing via unions:
union { float f; unsigned int i; } converter;
converter.f = someFloat;
someInt = converter.i;
I actually prefer this method quite a bit, but it too isn't fully supported by the standard. It's a very common compiler extension though, so in practice it's safe to use.

The only standards-compliant way to do this conversion is by casting the float to a char* and reconstructing it into an int one byte at a time:
compile_time_assert(sizeof(unsigned int) == sizeof(float)); // not guaranteed
unsigned char* src = (unsigned char*)&someFloat;
unsigned char* dest = (unsigned char*)&someInt;
for (int i = 0; i < sizeof(float); i++)
{
dest = src;
}
But this code is overbearing for the sake of ultra-correctness when the other two methods work with most known combinations of platform and compiler manufacturer.

(Type-punning == referencing a memory address by something other than its actual type.)

As stated, I'd prefer the union method to the in-place pointer casting, but if you really need to do this I suppose it's up to you which you choose.

Share this post


Link to post
Share on other sites
Quote:
Original post by exwonder
Most compilers support an extension to allow you do the same thing via unions:
union { float f; unsigned int i; } converter;
converter.f = someFloat;
someInt = converter.i;
I actually prefer this method quite a bit, but it too isn't fully supported by the standard. It's a very common compiler extension though, so in practice it's safe to use.

I would recommend casting over converting via a union since the former is permitted by the Standard (albeit with implementation-defined behaviour) and the latter is prohibited, even if it does work in practice. A cast is also clearer, especially if you use the form reinterpret_cast< int & >(x).

Σnigma

Share this post


Link to post
Share on other sites
Quote:
Original post by Enigma
I would recommend casting over converting via a union since the former is permitted by the Standard (albeit with implementation-defined behaviour) and the latter is prohibited, even if it does work in practice. A cast is also clearer, especially if you use the form reinterpret_cast< int & >(x).

I'll have to re-look up the union thing at some point.

reinterpret_cast is definitely a good solution, though, if I recall correctly... reinterpret_cast's from int to float are not allowed. So it won't be helpful in every situation where you'd want to manipulate the bits of a float directly.

As messy as it is, the C-style casting captures the fact that the conversion will most likely have to go between register sets via memory to happen, which is a nice side-effect, though one that most people won't be thinking about.

Share this post


Link to post
Share on other sites
I don't have a C compiler at hand, but wouldn't memcpy work?

memcpy(&i, &f, sizeof(f));

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!