Development Sandbox: Building A Better Math.pow() Function

I remember a long time ago I had read an article comparing two keys on a classic calculator. Someone had asked why the [Xy] key where y is equal to 2 is slower than the [X2] key. So I thought I’d have a look at the JVM’s equivalent function, Math.pow(). But also I wanted to see if I could create a better version of that function.

I finally wrote five versions of the power function to attempt to replace Java’s own Math.pow(). But I maxed their capabilities at the power of 9 for maximum flexibility. My initial tests showed a lot of promise. But with a lot more testing I was really impressed with my versions. Below are the five solutions that I came up with.

Simple Switch Statement

This solution is the simplest, although not the most eloquent. But it’s fast and simple.

public static double powerBSwitchOne(double value, int i) {
    switch (i) {
        case 9: return value * value * value * value * value * value * value * value * value;
        case 8: return value * value * value * value * value * value * value * value;
        case 7: return value * value * value * value * value * value * value;
        case 6: return value * value * value * value * value * value;
        case 5: return value * value * value * value * value;
        case 4: return value * value * value * value;
        case 3: return value * value * value;
        case 2: return value * value;
        case 1: return value;
        case 0: return 1;
        default: return -1;
    }
}

Cumulative Switch Statement

This is a simplified version of the above solution. Note the lack of break statements that causes each successive line to contribute to the final calculation. Although it’s clean and succinct, it’s not as fast.

public static double powerCSwitchTwo(double value, int i) {
    double result = value;

    switch (i) {
        case 9: result = result * value;
        case 8: result = result * value;
        case 7: result = result * value;
        case 6: result = result * value;
        case 5: result = result * value;
        case 4: result = result * value;
        case 3: result = result * value;
        case 2: return result * value;
        case 1: return value;
        case 0: return 1;
        default: return -1;
    }
}

Cumulative If Statement

This solution seems to have similar performance as the Cumulative Switch Statement. The only difference between the two is that the Java compiler generates a jump table at the top for each switch conditions. While with the if statement, the conditions appear next to their execution code.

public static double powerDIf(double value, int i) {
    double result = value;

    if (i == 9) result *= value;
    if (i >= 8) result *= value;
    if (i >= 7) result *= value;
    if (i >= 6) result *= value;
    if (i >= 5) result *= value;
    if (i >= 4) result *= value;
    if (i >= 3) result *= value;
    if (i >= 2) return *= value;
    if (i == 1) return value;
    if (i == 0) return 1;

    return -1;
}

Cumulative Ternary Operator

I love the ternary operator because it’s so succinct, generates less bytecode and can easily be embedded inline.

public static double powerETernary(double value, int i) {
    double result = i == 9 ? value * value : value;
    result = i >= 8 ? result * value : result;
    result = i >= 7 ? result * value : result;
    result = i >= 6 ? result * value : result;
    result = i >= 5 ? result * value : result;
    result = i >= 4 ? result * value : result;
    result = i >= 3 ? result * value : result;
    return i >= 2 ? result * value : i == 1 ? value : i == 0 ? 1 : -1;
}

For Loop

The trusty for loop. It’s simple and looks fast. But this was the slowest of the five methods. Even after 3000 iterations. But for the odd execution here and there, it’s in the middle of the road.

public static double powerFForLoop(double value, int i) {
    if (i >= 0) {
        double result = 1;
        for (int j = i; j >= 1; j--) {
            result *= value;
        }
        return result;
    } else {
        return -1;
    }
}

Execution

I ran each of the above functions for 3000 iterations to give the JVM enough data to optimize the code. For each iteration, each function was called 10 times for powers of 0 to 9. The first iteration results looked exciting for the functions I had written. And the 3000th iteration results were surprising. Just note that when calculating performance times, the numbers will vary with each test. But the results were consistent across most tests.

What did surprise me was the performance of the Math.pow() method which consistently clocked in at around 175,000ns for the first iteration for ten executions. My slowest method ran at about 26,000ns. So I was impressed with my code but disappointed with Java’s own implementation.

graph
Execution Times of each function in Nanoseconds

The 3,000th iteration was the grand equalizer. Rach method clocked in at under 500ns. So now I knew that I could write a better version of a Math.pow() method. There’s obviously more to this story when it comes to JVM’s performance skills. But the key takeaway here is that I was able to write faster versions of the Math.pow() method.

I’m not suggesting that you go out and write a method or use one of mine. But it may be advantageous to simply multiply your own calculation inline instead of calling Java’s own version. Plus, this opens the door to investigate other methods that could be improved. But having said that, all the numbers above are in nanoseconds. So Math.pow() is still a very fast method overall. How much time you want to invest in building a better wheel is all up to you. And pragmatically, may not be worht the effort. But it’s still interesting.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s