15

I've heard of the "fast inverse square root", discussed here, and I wanted to put it in my Java program (just for research purposes, so ignore anything about the native libraries being faster).

I was looking at the code, and the C code directly converts the float into an int with some C pointer magic. If you try to do this in Java with casts, it doesn't work: java truncates the float (as you would expect), and you can't get the pointer of a primitive (as you can in C). So how do you do this?

Community
  • 1
  • 1
Riking
  • 2,149
  • 19
  • 33

2 Answers2

23

Remember to benchmark your code before using this.

If it turns out you don't need it, or it's slower on the CPU architecture you are using, then it's better to go without having this obtuse code in your project.


The Java libraries have a way to get from the float number to the raw bits.

As seen in the Javadoc for java.lang.Float ( http://docs.oracle.com/javase/6/docs/api/java/lang/Float.html ), we have the floatToIntBits function, as well as intBitsToFloat.

This means we can write the "fast inverse square root" in Java as follows:

public static float invSqrt(float x) {
    float xhalf = 0.5f * x;
    int i = Float.floatToIntBits(x);
    i = 0x5f3759df - (i >> 1);
    x = Float.intBitsToFloat(i);
    x *= (1.5f - xhalf * x * x);
    return x;
}

Here is the version for doubles:

public static double invSqrt(double x) {
    double xhalf = 0.5d * x;
    long i = Double.doubleToLongBits(x);
    i = 0x5fe6ec85e7de30daL - (i >> 1);
    x = Double.longBitsToDouble(i);
    x *= (1.5d - xhalf * x * x);
    return x;
}

Source: http://www.actionscript.org/forums/showthread.php3?t=142537

RamenChef
  • 5,533
  • 11
  • 28
  • 39
Riking
  • 2,149
  • 19
  • 33
  • Anyone know the derivation of 0x5fe6ec85e7de30daL from 0x5f3759df? – micycle Jul 11 '20 at 22:56
  • @micycle The derivation of these particular constants is lost to time, and these are not actually the optimal constants - merely the famous ones. You can find the optimal constant search code somewhere. – Riking Jul 25 '20 at 07:28
6

For Riking's answer, even the double one can return stuff like 0.9983227945440889 for the square root of one.

To increase accuracy, you can use this version of it I made:

public static double Q_rsqrt(double number){
    double x = number;
    double xhalf = 0.5d*x;
    long i = Double.doubleToLongBits(x);
    i = 0x5fe6ec85e7de30daL - (i>>1);
    x = Double.longBitsToDouble(i);
    for(int it = 0; it < 4; it++){
        x = x*(1.5d - xhalf*x*x);
    }
    x *= number;
    return x;
}

You can edit how long before the for loop terminates however you want, but 4 times seems to get it down to the maxiumum accuracy for a double. If you want perfect accuracy (or if long strings of decimals where they shouldnt be bother you), use this version.

Stevoisiak
  • 16,510
  • 19
  • 94
  • 173
  • I don't understand why but the loop does the trick and solve the problem of strange value `0.998...`. Thanks – 56ka May 24 '15 at 00:00
  • 1
    @56ka the line inside the loop is an iteration of Newton's Method. By adding 3 more iterations, it improves accuracy at the expense substantially more operations. – Dusty Jul 30 '15 at 16:15
  • Actually, now that I'm looking more closely, isn't this computing sqrt instead in inverse sqrt? Obviously 1.0 has the problem of those being the same, but if I still 4.0 into your code, it gives me 2. The last x*=number should be dropped for invSqrt – Dusty Jul 30 '15 at 16:22