This code snipped is obviously the fast inverse square root. The pointer semantics there are not really used to do pointer things, but to reinterpret the bits at a certain memory location as a different type.
If you were to assign i=y
this would be turned into a truncating conversion from floating point to integer. This however is not what's desired here. What you actually want is raw access to the bits, which is not straightforward possible on a floating point typed variable.
Let's break this statement down:
i = * ( long * ) &y;
&y
: address of y. The type of this expression is (float*)
.
(long*)
: cast to type. Appled to &y
it steamrolls over the information, that this is the address of a floating point typed object.
*
: dereference, which means, "read out" whatever is located at the address given and interpret as the base type of the pointer that's being dereferenced. We've overwritten that to be (long*)
and essentially are lying to the compiler.
For all intents and purposes this breaks pointer aliasing rules and invokes undefined behaviour. You should not do this (caveats apply¹).
The somewhat well defined way (at least it doesn't break pointer aliasing rules) to do such trickery is by means of a union
.
float Q_rsqrt( float number )
{
union {
float y;
long i;
} fl;
float x2;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
fl.y = number;
fl.i = 0x5f3759df - ( fl.i >> 1 ); // ???
fl.y = fl.y * ( threehalfs - ( x2 * fl.y * fl.y ) ); // 1st iteration
// fl.y = fl.y * ( threehalfs - ( x2 * fl.y * fl.y ) ); // 2nd iteration, this can be removed
return fl.y;
}
EDIT:
It should be noted, that the type-punning via union as illustrated above is not sanctioned by the C language standard as well. However unlike language undefined behavior the standard so far leaves the details of the kind of union accesses done in that way as implementation dependent behavior. Since type-punning is something required for certain tasks, I think a few proposals had been made to make this well defined in some upcoming standard of the C programming language.
For all intents and purposes practically all compilers support the above scheme, whereas type-punning via pointer casts will lead to weird things happening if all optimization paths are enabled.
1: Some compilers (old, or custom written, for specific language extensions – I'm looking at you CUDA nvcc) are severly broken and you actually have to coerce them with this into doing what you want.