Given a number ,write a program that calculates the inverse square root:
Well, it's not that hard:
float inverse_sqrt(float x){
return 1.0/sqrt(x);
}
So ... no big deal, right ? Well, maybe not for our current processors embedded with special Floating Operation Units, but old computers with their traditional ALU's had a hard time calculating such task of division and square root. And for old games like Doom and Quake III, calculating efficiently the inverse square root of a number made all the difference in the Physics Engine of the game. The reason for this is because Physics Engines have to perform lot's of vector normalization in order to apply, for example, Snell's Law of refraction correctly for lighting.
Given a vector , it's norm is and therefore after normalization it becomes (). And for every frame of the game, hundreds of thousands of this calculations are made. So, how to efficiently calculate this ? Well, Jonh Carmack leading programmer of idSoftware at the development of Quake III, came up with the following algorithm used in the game (comments are from the original source code) :
float Q_rsqrt(float number){
long i;
float x2,y;
const float threehalfs = 1.5F;
x2 = number*0.5F;
y = number;
i = * (long *) &y; // evil floating point bit hack
i = 0x5f3759df - (i >> 1); // what the fuck ?
y = * (float *) &i;
y = y * (threehalfs - (x2 * y * y)); // 1st iteration
// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, can be removed
return y;
}
Yeah, looks way more complicated than the previous code, and the way it works just shows what a hell of a programmer Carmack was. In order to understand the algorithm, some things have to be clear first:
*How Floating Points Are Represented in 32 bits
How to represent 696969 in binary ? Well, its just . Now what about ? The Standard way is how the IEEE 754 describes: Exactly hot we represent in scientific notation !
For example, in base 10: , And in binary: , According to the triple IEE 754 Standard, the first bit represents the sign ( or ), the next 8 bits represents the exponent, and the next 23 bits represents the Mantissa, the number that comes before the exponent in the binary scientific representation. The following picture makes more clear:

That's how the standard specifies how to interpret these 32 bits for Float Numbers. And thererefore, to get the actual number from these 32 bits, we calculate , from the 23 first bits, , from the next 8, and finally the last sign, , and plug these guys in the formula:
The is necessary to calculate negative exponents, since goes from to ,we can have the interval .
Since we are calculating positive floating point number for the problem of vector normalization (all guys are squared up), we can leave the representation just as
Now for no particular reason, let's calculate , in base 2:
For the term , since belong to , we can approximate the term using the Taylor Approximation: , where is a is a correction term. And therefore, rearranging terms:
The original programmers of Quake calculated that was a optimal for approximating the original function.
Now, a important note, is exactly how you represent this float number in binary:

The Evil Bit Hack
We can't do bit tricks with floating numbers, since they are tied to IEEE 754. But with integers we can:
int p = 4;
p = (p >> 1); // 2 , shift left divide by 2
p = (p << 1) // 4, shift right multiply by 2
So how can we trick C to make bit manipulation with floats ? We can't just do
float f = 3.33;
int i = (int) f;
Because we lose information about the floating number in the convertion.
Here's the magic. Take for example , with binary representation:
if we make a hard conversion, it becomes:
But, what if the C language inteprets as a integer, not a float ? Then it would interpreted as . How to do this? We trick C:
float f = 3.33
int i = *( int *)&f;
printf("%d",i); //prints 1079320248
We first fool C to think that the adress where is located points to a int, not a float, and to extract this information, we point again, thats exactly whats going on line 2. Now we can make bit tricks with the binary representation of floats.
Finally, Knowing all of this, the algorithm can be understood.
W T F
We want to calculate
Applying log to both sides:
using the previous log representation:
After a long boring rearrangement of terms, we have:
Considering that is just the binary representation of , (the same for ) then:
Calculating for and converting to hexadecimal:
And therefore we transformed the original function involving division and square rooting to one only involving subtraction and bit shifting! we can do the shift because of the evil bit trick :)
To get the actual number in float from the binary representation, we undo the trick:
y = * (*float)&x;
And that's it! .... Well , not quite, there's one more optimzation we can do to enhance the approximation:
Newton's Method
Newton's method is a numeric procedure to have an approximation of the root of a function.
The following image makes it more clear:

So, given a number , we iteratively calculate new 's until we have in the following way:
is root for:
For
So,
So,
Which is exactly the last line of code. Since we already have a good approximation, one iteration is enough. And that's it !!! Personally this was a very cool trick to learn, how algorithms could overcome the hardware limitations at the time in smart ways :)