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

Started by
21 comments, last by Chris Lomont 16 years, 1 month ago
In Java, you can do this:

float f = 3.141592654f;int i = Float.floatToIntBits(f);


Ah, one of those rare cases where a high level language does a low level thing better than C/C++ :)
Advertisement
Code like this scares the devil out of me. If you want to do yourself a favour, never use such constructs. They may work now, or they may appear to work, but eventually at some point in the future, it will fail (and probably you will not even know what hit you).

Hacks are ok if they are needed. They still aren't pretty, but if they get you what you can't otherwise achieve, that's ok (for me at least, opinions differ).
However, in this case, it isn't needed at all. Using SSE math (or similar) is a much better alternative. Two steps of Newton-Raphson will take around a dozen cycles. I doubt that the above code runs significantly faster than that.
A memcpy might work, but it would totally defeat the purpose with a function call.
Quote:Original post by samoth
Code like this scares the devil out of me. If you want to do yourself a favour, never use such constructs. They may work now, or they may appear to work, but eventually at some point in the future, it will fail (and probably you will not even know what hit you).

Hacks are ok if they are needed. They still aren't pretty, but if they get you what you can't otherwise achieve, that's ok (for me at least, opinions differ).
However, in this case, it isn't needed at all. Using SSE math (or similar) is a much better alternative. Two steps of Newton-Raphson will take around a dozen cycles. I doubt that the above code runs significantly faster than that.


I second the above post.

That said, good advice is not always heeded for the simple fact that people will always want to know if something can be done...so, expect dodgy hacks to hang around forever -- because always someone will want to know if they can pull something off faster or more efficiently than the 'safe' version. Of course, if we all end up progressing to managed code, perhaps crap like this won't come up anymore (though I doubt it) :P

~Shiny
------------'C makes it easy to shoot yourself in the foot. C++ makes it harder, but when you do, it blows away your whole leg.' -Bjarne Stroustrup
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.


Huhu, that's funny, because in the DirectX documentation, they recommend the use of :

*(DWORD *)&floatValue

to pass floating values as DWORD render states values :)

(anyway I agree it's ugly, the first time I saw this in the documentation, I believed it was a sort of joke :p)
Quote:Original post by paic
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.

Huhu, that's funny, because in the DirectX documentation, they recommend the use of :
*(DWORD *)e
to pass floating values as DWORD render states values :)
(anyway I agree it's ugly, the first time I saw this in the documentation, I believed it was a sort of joke :p)


The point here is that it does work dependably on almost all platforms, and is used by most software. Strict aliasing never really seemed to catch on, so there aren't in practice many problems with it.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by paic
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.


Huhu, that's funny, because in the DirectX documentation, they recommend the use of :

*(DWORD *)&floatValue

to pass floating values as DWORD render states values :)

(anyway I agree it's ugly, the first time I saw this in the documentation, I believed it was a sort of joke :p)


That is kind of funny and kind of tragic. Microsoft has encouraged a lot of bad practices over the years. As we all know, we shouldn't use that as a sign of generally applicable acceptable style. I do see people justify things in this manner, at times. I had to untrain some of my coworkers from disrespecting the type system (still a work in progress, at times). My general advice is if you have to do something ugly and possibly non-portable, I suggest using asserts to at least have some confidence that it did what you think it did and hide it off in a prettier facade-like piece of code so clients don't have to be affected by the ugliness.
Quote:Original post by Boder
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.


It's also processor dependent due to byte order. IEEE 754 specifies bit order, but does not specify how the bytes that make up those bits gets laid out in memory. It can be the same endianness as integer representation on a processor or it may not. If it's not then the bit shift isn't going to behave as expected.
A few points:

1. Both unions and casting have their issues. You only use code like this when you have a test before hand to catch if it fails. This test needs only be done once when the app starts, and warns you. This way you catch compiler differences, architecture differences, etc.

2. You should not use a signed int, since right shift of a signed int is implementation defined.

3. Also signed ints are not always twos complement. There are at least three (if I recall) possible ways a C compiler can store signed ints.

4. Obviously some architectures dont have 32 bit ints, and this will also break there.

Hence, use an unsigned int, and if you need to put stuff like this in your code, include a test function that gets called that tests all your assumptions. Then your code does not introduce hard to track down bugs, because every time you switch compilers/platforms your tests quickly catch the assumption changes.

To those of you saying never to use such constructs, you are missing the point. Sometimes you *NEED* a little more performance, in which case you do stuff like this. Saying never to use it is like saying never to use inline assembly, never unroll loops, never reorganize clean code to messy code to optimize cache issues, etc. The whole point of code like this is a simple 1.0f/sqrt(val) is simply way too inefficient for some uses.

For those unfamiliar with the issues, google the constant in the code and read up.
Chris Lomontwww.lomont.org
Does an in-depth discussion of type-punning (especially in C) and this *(int*)&x sort of construct exist somewhere? I can't seem to find one. Just bits and pieces. Wikipedia seems to have the largest chunk of info in one place but it's pretty weak.

This topic is closed to new replies.

Advertisement