Original Post
Well, I'm looking for the double version of the famous function invSqrt() of the Quake 3 Engine. Could someone provide it? Thanks. [Edited by - johnstanp on July 5, 2008 6:41:02 PM]
inline double invSqrt( const double& x ){ double y = x; double xhalf = ( double )0.5 * y; long long i = *( long long* )( &y ); i = 0x5fe6ec85e7de30daLL - ( i >> 1 );//LL suffix for (long long) type for GCC y = *( double* )( &i ); y = y * ( ( double )1.5 - xhalf * y * y ); return y;}long long i = *( long long* )( &y ); // pointer cast y = *( double* )( &i ); // pointer cast( i >> 1 ) // (not strictly UB, but right shifts are implementation depenedent for signed integersQuote:
Original post by rozz666
This function leads to undefined behaviour. Particularly:long long i = *( long long* )( &y ); // pointer cast y = *( double* )( &i ); // pointer cast( i >> 1 ) // (not strictly UB, but right shifts are implementation depenedent for signed integers
Apart from that, it relies on implemenation specific factors like double implementation and size, long long size, etc.
Lastly, try to profile what's really faster. My bet is that 1/std::sqrt(x) should be faster on modern CPUs.
Quote:
Original post by h3ro
I read an article about that function some time ago, in that article there was a warning about the speed of it. It was a lot faster to use "back then", but as compilers have gotten better, that code might actually make your program run slower.
Quote:
Original post by dmatter
Optimising things like square roots is an old trick - in modern programming, we would prefer to optimise our use of square roots instead: try to change your high level algorithms so that they make more economical use of such maths functions.
Quote:I tend to think that thing are a bit more complicated than that. True, new algorithms are typically the only way to improvements beyond an order of magnitude, but on the other hand implementing more complex specialized algorithms will affect far more code and is usually more brittle. That is to say that if this (admittedly bizarre) bithack allows you not to re-engineer dozens of systems around the game to keep values normalized then you've saved a great deal of work, at the cost of figuring out whether the hack is reliable.
Original post by dmatter
Optimising things like square roots is an old trick - in modern programming, we would prefer to optimise our use of square roots instead: try to change your high level algorithms so that they make more economical use of such maths functions.
Quote:Odd.. What's your x87 precision set to?
Original post by rozz666
On my CPU (Pentium 4 1.4 GHz) invSqrt runs about 20% slower than 1 / std::sqrt.
As for the pointer casts - it's undefined behaviour. It's undefined what will happen. Sure, many compilers allows this, but then you have to check your code with every compiler you use and still can't be sure when it's going to fail.
Quote:
Original post by rozz666
On my CPU (Pentium 4 1.4 GHz) invSqrt runs about 20% slower than 1 / std::sqrt.
Quote:Dude, if the way the initial guess is made by shifting the exponent and mantissa together and adjusting the result with a magic constant *isn't* a bithack then I don't know what is. I seriously doubt that the IEEE committee had this kind of abuse in mind when choosing their format, or anything much beyond the ability to easily compare floats as integers really.
Original post by Raghar
It's not a bit hack, it's a reasonable implementation of some algorithm. The standard organisation made FP64 in a way to permit algorithms like this.
Though quite a lot of people doesn't have the right education to know these algorithms (from experience).
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 level hacking 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, this can be removed #ifndef Q3_VM #ifdef __linux__ assert( !isnan(y) ); // bk010122 - FPE? #endif #endif return y;} double InvSqrt(double number){ __int64 i; double x2, y; const double threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( __int64 * ) &y i = 0x5fe6ec85e7de30da - ( i >> 1 ); y = * ( double * ) &i y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y;} Quote:
Original post by rozz666
As for the pointer casts - it's undefined behaviour. It's undefined what will happen. Sure, many compilers allows this, but then you have to check your code with every compiler you use and still can't be sure when it's going to fail.
Quote:Enter: Unit tests ;)
Original post by rozz666
Sure, many compilers allows this, but then you have to check your code with every compiler you use and still can't be sure when it's going to fail.
Quote:
Original post by lol Quote:
Original post by rozz666
As for the pointer casts - it's undefined behaviour. It's undefined what will happen. Sure, many compilers allows this, but then you have to check your code with every compiler you use and still can't be sure when it's going to fail.
Can't you get the same result without the undefined behavior by using a union instead of a pointer cast?
Quote:
Original post by Ra Quote:
Original post by lol Quote:
Original post by rozz666
As for the pointer casts - it's undefined behaviour. It's undefined what will happen. Sure, many compilers allows this, but then you have to check your code with every compiler you use and still can't be sure when it's going to fail.
Can't you get the same result without the undefined behavior by using a union instead of a pointer cast?
No. Accessing a union through alternate members is undefined behavior. Once you set a union via a certain member you're only supposed to access it through that member.
Using unions to convert between types is an all too common abuse which few people know about.
Back on topic, any processor that supports SSE already has an inverse square root instruction built in, RSQRTSS. You can access it via a compiler intrinsic (_mm_rsqrt_ss) in MSVC 2008. However, even using the old-style FPU math will outperform Quake's inverse square root on any modern processors.
This kind of thing is a micro-optimization that shouldn't be performed until you've determined, with a profiler, that you actually need it. Which means the profiler is reporting that you're spending 80% of your time on sqrt calls. Writing out a square root function using integer math and bit hacks is a really bad idea, as it'll only turn out much slower than the built-in instructions on modern processors.
This topic has been locked by a moderator. New replies are not allowed.
With your permission, GameDev.net uses analytics cookies to understand how people use the platform. You can accept analytics or continue with necessary cookies only. Learn more