views:

202

answers:

5

From this article. Here's the code:

float InvSqrt(float x){ // line 0
   float xhalf = 0.5f * x;
   int i = *(int*)&x; // store floating-point bits in integer
   i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method
   x = *(float*)&i; // convert new bits into float
   x = x*(1.5f - xhalf*x*x); // One round of Newton's method
   return x;
}

...I can't even tell if that's C or C++. [okay apparently it's C, thanks] Could someone translate it to Java for me, please? It's (only, I hope) lines 2 and 4 that are confusing me.

+2  A: 

The code you quote is C, although the comments are C++-style.

What the code is doing involves knowledge of the way floating-point values are stored, at the bit level. The "magic number" 0x5f3759d5 has something do with a particular value.

The floating point x value's bits are accessed when i is initialized, because the address of x is dereferenced. So, i is loaded with the first 32 bits of the floating point value. On the next line, x is written with the contents of i, updating the working approximation value.

I have read that this code became popular when John Carmack released it with Id's open source Quake engine. The purpose of the code is to quickly calculate 1/Sqrt(x), which is used in lighting calculations of graphic engines.

I would not have been able to translate this code directly to Java because it uses "type punning" as described above -- when it accesses the float in memory as if it were an int. Java prevents that sort of activity, but as others have pointed out, the Float object provides methods around it.

The purpose of using this strange implementation in C was for it to be very fast. At the time it was written, I imagine a large improvement came from this method. I wonder if the difference is worth it today, when floating point operations have gotten faster.

Using the Java methods to convert float to integer bits and back may be slower than simply calculating the inverse square root directly using Java math function for square root.

Heath Hunnicutt
You can treat float as integer in Java - see my upcoming comment below. Edit - Michael beat me too it - see his! :)
glowcoder
treating a float as an int isn't what the OP wants. That would truncate. they need the floattointbits method as others describe.
Brian Postow
@Brian, in a sense it is - and that's what I was going to do - Michael just beat me too it.
glowcoder
@glowcode-1 That was news to me, but down-voting over such a trivial oversight seems a little kiddyish. Most important point is that OP probably should not use the bit pattern method; do you think it is still the faster way to get the value compared to just calculating 1/sqrt(x)?
Heath Hunnicutt
@Heath: down-voting is not a personal attack on the author. It's simply a gesture that says that the given answer is not useful in answering the question at hand. It doesn't even mean that the answer is necessarily incorrect.
Joachim Sauer
OP here. I'm going to see if it's faster... mostly I wanted to know how it worked. Also. Down votes are not evil! Your response was truly Not Useful - that's what they're for!
Vuntic
@Heath: Today, on x86 there's no need for this trick due to the widespread availability of processors with the rsqrtss/rsqrtps instructions. FWIW, GCC can use these instructions automatically sometimes, see the -mrecip option.
janneb
@Joachim -- A good point, the same point I made when I referred to the fact that, overall, this answer has a more important point than whether the poster CAN pun the types; SHOULD he pun the types? Almost definitely, no. If people disagree with rewriting the code and -1, OK, but I didn't have that impression -- more that we were involved in some point scoring over floatToIntBits...
Heath Hunnicutt
@Vuntic - If it was truly Not Useful, please then do not try the experiment I advised. Go with the code from other answers, it should be fast enough and easy for you to maintain. But please don't waste your time following my non-useful advice. In return, I'll keep an eye out for you and not waste your time with my answers.
Heath Hunnicutt
I should make it clear my downvote was not for the trivial oversight - it was for saying the operation couldn't be ported directly, when indeed it can be, line by line. I removed the downvote once it was clarified.
glowcoder
Right... this is the Internet. Sorry. It's way too easy to offend people here, myself included. >.<
Vuntic
+10  A: 

You want to use these methods:

And there may be issues with strictfp, etc.

It's roughly something like this: (CAUTION: this is untested!)

float InvSqrt(float x){ // line 0
   float xhalf = 0.5f * x;
   int i = Float.floatToIntBits(x); // store floating-point bits in integer
   i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method
   x = Float.intBitsToFloat(i); // convert new bits into float
   x = x*(1.5f - xhalf*x*x); // One round of Newton's method
   return x;
}
polygenelubricants
Thanks for the warning about strictfp.
Vuntic
@Vuntic: there are many Q/As on `strictfp` in stackoverflow. I don't really know the issue in too great of a detail, I just have a hunch that it may be something important. Or maybe it's irrelevant =)
polygenelubricants
+7  A: 

Those lines are used to convert between float and int as bit patterns. Java has static methods in java.lang.Float for that - everything else is identical.

static float InvSqrt(float x) { // line 0
    float xhalf = 0.5f * x;
    int i = Float.floatToIntBits(x); // store floating-point bits in integer
    i = 0x5f3759d5 - (i >> 1); // initial guess for Newton's method
    x = Float.intBitsToFloat(i); // convert new bits into float
    x = x * (1.5f - xhalf * x * x); // One round of Newton's method
    return x;
}
Michael Borgwardt
@Michael: would you comment on `strictfp`? Is it an issue in this context, since we're playing with the bits etc?
polygenelubricants
Theoretically `strictfp` would make the result consistent in a guaranteed manner. Whether or not it is necessary depends on whether or not the original code depended on a specific floating point implementation or not.
Joachim Sauer
@polygenelubricants: I don't think strictfp would be more of an issue here than elsewhere. The "playing with the bits" part happens entirely before the value undergoes any floating-point arithmetic.
Michael Borgwardt
A: 

The lines you care about are pretty simple. Line 2 takes the bytes in float x, which are in some floating-point representation like IEEE754, and stores them in an integer, exactly the way they are. This will result in a totally different number, since integers and floats are represented differently in byte form. Line 4 does the opposite, and transfers the bytes in that int to the float again

Michael Mrozek
+1  A: 

Ok I'm going out on a limb here, because I know C but I don't know Java.

Literally rewriting this C code in Java is begging for trouble. Even in C, the code is unportable. Among other things it relies on: The size of floating point numbers. The size of integers. The internal representation of floating point numbers. The byte alignment of both floating point numbers and integers. Right shift ( i.e. i>>1) being implemented using logical right shift as opposed to arithmetic right shift (which would shift in a 1 on integers with a high order 1 bit and thus no longer equate to divide by 2).

I understand Java compiles to a bytecode rather than directly to machine code. Implementers of byte code interpreters tune using assumptions based on the spec for the byte code and an understanding of what is output by the compiler from sensible input source code.

Hacks like this don't fall under the umbrella "sensible input source".

There is no reason to expect the interpreter will perform faster with your C hack, in fact there is a good chance it will be slower.

My advice is: IGNORE The C code.

Look for a gain in efficiency that is Java centric.

The concept of the C hack is:

Approximate 1/square(x) by leveraging knowledge that the internal representation of floating point numbers already has the exponent broken out of the number, exponent(x)/2 is faster to compute than root(x) if you already have exponent(x).

The hack then performs one iteration of newton's method to reduce the error in the approximation. I Presume one iteration reduced the error to something tolerable.

Perhaps the concept warrants investigation in Java, but the details will depend on intimate knowledge of how JAVA is implemented, not how C is implemented.

pbernatchez
The code may be unportable in C, but it's perfectly safe in Java, because all the things you mention are tightly specified there. And I see no reason why this would not be much faster in Java as well, if the limited precision it yields is all you need.
Michael Borgwardt
Unless you post the *Java* code which would be safe and fast, your comment is just background noise. The C code is not for Java consumption. The point is: don't look at C implementation details to work out Java implementation details. They will be different.
pbernatchez