The code below performs a fast inverse square root operation by some bit hacks. The algorithm was probably developed by Silicon Graphics in early 1990's and it's appeared in Quake 3 too. more info
However I get the following warning from GCC C++ compiler: dereferencing type-punned pointer will break strict-aliasing rules
Should I use static_cast
, reinterpret_cast
or dynamic_cast
instead in such situations?
float InverseSquareRoot(float x)
{
float xhalf = 0.5f*x;
int32_t i = *(int32_t*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
Take a look at this for more information on type punning and strict aliasing.
The only safe cast of a type into an array is into a
char
array. If you want one data address to be switchable to different types you will need to use aunion
I'm adding an answer not to refute the accepted answer, but to augment it. I believe the accepted answer is both correct and efficient (and I've just upvoted it). However I wanted to demonstrate another technique that is just as correct and efficient:
Using clang++ with optimization at -O3, I compiled plasmacel's code, R. Martinho Fernandes code, and this code, and compared the assembly line by line. All three were identical.
Update
I no longer believe this answer is correct, due to feedback I've gotten from the committee. But I want to leave it up for informational purposes. And I am purposefully hopeful that this answer can be made correct by the committee (if it chooses to do so). I.e. there's nothing about the underlying hardware that makes this answer incorrect, it is just the judgement of a committee that makes it so, or not so.
There are a few good answers here that address the type-punning issue.
I want to address the "fast inverse square-root" part. Don't use this "trick" on modern processors. Every mainstream vector ISA has a dedicated hardware instruction to give you a fast inverse square-root. Every one of them is both faster and more accurate than this oft-copied little hack.
These instructions are all available via intrinsics, so they are relatively easy to use. In SSE, you want to use
rsqrtss
(intrinsic:_mm_rsqrt_ss( )
); in NEON you want to usevrsqrte
(intrinsic:vrsqrte_f32( )
); and in AltiVec you want to usefrsqrte
. Most GPU ISAs have similar instructions. These estimates can be refined using the same Newton iteration, and NEON even has thevrsqrts
instruction to do part of the refinement in a single instruction without needing to load constants.Forget casts. Use
memcpy
.The original code tries to initialize the
int32_t
by first accessing thefloat
object through anint32_t
pointer, which is where the rules are broken. The C-style cast is equivalent to areinterpret_cast
, so changing it toreinterpret_cast
would not make much difference.The important difference when using memcpy is that the bytes are copied from the
float
into theint32_t
, but thefloat
object is never accessed through anint32_t
lvalue, becausememcpy
takes pointers to void and its insides are "magical" and don't break the aliasing rules.The cast invokes undefined behaviour. No matter what form of cast you use, it will still be undefined behaviour. It is undefined no matter what type of cast you use.
Most compilers will do what you expect, but gcc likes being mean and is probably going to assume you didn't assign the pointers despite all indication you did and reorder the operation so they give some strange result.
Casting a pointer to incompatible type and dereferencing it is an undefined behaviour. The only exception is casting it to or from char, so the only workaround is using
std::memcpy
(as per R. Martinho Fernandes' answer). (I am not sure how much it is defined using unions; It does stand a better chance of working though).That said, you should not use C-style cast in C++. In this case,
static_cast
would not compile, nor woulddynamic_cast
, forcing you to usereinterpret_cast
andreinterpret_cast
is a strong suggestion you might be violating strict aliasing rules.Based on the answers here I made a modern "pseudo-cast" function for ease of application.
C99 version (while most compilers support it, theoretically could be undefined behavior in some)
Universal versions (based on the accepted answer)
Cast types with the same size:
Cast types with any sizes:
Use it like: