| |||||||||
Approximation of pow() in Java, C and C++
Approximation of pow() in Java, C and C++For many application of thepow() method, an approximate value suffice. The below text shows how to gain a 41 times speed increase by
using an approximated value.
Approximation of pow() in Java
public static double pow(final double a, final double b) {
final int x = (int) (Double.doubleToLongBits(a) >> 32);
final int y = (int) (b * (x - 1072632447) + 1072632447);
return Double.longBitsToDouble(((long) y) << 32);
}
This is really very compact. The calculation only requires 2 shifts, 1 mul, 2 add, and 2 register operations. That's it! In my tests it usually within an error margin of 5% to 12%, in extreme cases sometimes up to 25%. A careful analysis is left as an exercise for the reader. This is very usable for in e.g. metaheuristics or neural nets. I use Linux, Java 1.6.0-b105 with the server VM, and execute the benchmark with this command:
The approximation is 27 times faster than Approximation of pow() in C and C++double pow(double a, double b) {
int tmp = (*(1 + (int *) &a));
int tmp2 = (int)(b * (tmp - 1072632447) + 1072632447);
double p = 0.0;
*(1 + (int * ) &p) = tmp2;
return p;
}
Compiled on my Pentium-M with gcc 4.1.2:
gcc -O3 -march=pentium-m -fomit-frame-pointer -fno-strict-aliasing
This version is 7.8 times faster than pow() from the standard library. WARNING! you HAVE to use the How the Approximation was DevelopedIt is quite impossible to understand what is going on in this function, it just magically works. To shine a bit more light on it, here is a detailed description how I have developed this.Approximation of e^xAs described here, the paper "A Fast, Compact Approximation of the Exponential Function" develops a C macro that does a good job at exploiting the IEEE 754 floating-point representation to calculatee^x.
This macro can be transformed into Java code straightforward, which looks like this:
public static double exp(double val) {
final long tmp = (long) (1512775 * val + (1072693248 - 60801));
return Double.longBitsToDouble(tmp << 32);
}
Use Exponential Functions for a^bThanks to the power of math, we know thata^b can be transformed like this:
Now we have expressed the pow calculation with Approximation of ln(x)Here comes the big trick: Remember that we have the nicee^x approximation? Well, ln(x)
is exactly the inverse function! That means we just need to transform the above approximation so that the output of
e^x is transformed back into the original input.
That's not too difficult. Have a look at the above code, we now take the output and move backwards to undo the calculation. First reverse the shift: final double tmp = (Double.doubleToLongBits(val) >> 32);Now solve the equation tmp = (1512775 * val + (1072693248 - 60801)) for val:
Voíla, now we have a nice approximation of public double ln(double val) {
final double x = (Double.doubleToLongBits(val) >> 32);
return (x - 1072632447) / 1512775;
}
Combine Both ApproximationsFinally we can combine the two approximations intoe^(ln(a) * b):
public static double pow1(final double a, final double b) {
// calculate ln(a)
final double x = (Double.doubleToLongBits(a) >> 32);
final double ln_a = (x - 1072632447) / 1512775;
// ln(a) * b
final double tmp1 = ln_a * b;
// e^(ln(a) * b)
final long tmp2 = (long) (1512775 * tmp1 + (1072693248 - 60801));
return Double.longBitsToDouble(tmp2 << 32);
}
Between the two shifts, we can simply insert the public static double pow2(final double a, final double b) {
final double x = (Double.doubleToLongBits(a) >> 32);
final long tmp2 = (long) (1512775 * (x - 1072632447) / 1512775 * b + (1072693248 - 60801));
return Double.longBitsToDouble(tmp2 << 32);
}
Now simplify
The ResultThat's it! Add some casts, and the complete function is the same as above.public static double pow(final double a, final double b) {
final int tmp = (int) (Double.doubleToLongBits(a) >> 32);
final int tmp2 = (int) (b * (tmp - 1072632447) + 1072632447);
return Double.longBitsToDouble(((long) tmp2) << 32);
}
This concludes my little tutorial on micro optimization of the pow() function. If you have come this far, I congratulate your persistence :-) Martin Ankerl, http://martin.ankerl.com/AddendumIn my experience the error gets bigger when the exponent gets bigger. I have written a short program that uses random numbers to try to find a combination that gets very bad errors. Base is a random double between 0 and 1000, exponent random double between 0 and 5. The biggest error after 100 million evaluations is:a between 0 and 1000, b between 0 and 5: Average error is 4.021374964371438 % --- Average error is 2.7778168699408558 % ---I am using this (a modified form) in an Ant Colony Optimization algorithm. There the core algorithms needs to calculate a^b * c^d very often. I have done comparison of the optimization quality when using this optimization and when using Math.pow(), the error does not have any measurable negative effect in the examples I am using. And the optimization got a hell of a lot faster, so even if there is a slight decrease in optimization quality this is more than compensated by the higher number of iterations I am able to do now. For additional comments see http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/If you like this site consider CommentsIf you have any comments to this article, please drop me a mail at firstclassthoughts at gmail dot com please indicate if I can't publish whole or parts of your comment on the site.
If you like this site consider Help spread the wordShare this post on your favorite social bookmarking sites:
The most recent contributions 28/07/09 Magic in mathematics II Fun with the number cyclic numbers, and specifically with 142857 as it is the smallest of such numbers. 13/07/09 My top 8 time-saving Firefox shortcuts This article presents my favorite top 8 time-saving shortcuts in Firefox 3.0 and Firefox 3.5. Get to know these and you'll be saving a lot of time. They have been ordered by "the element of most surprise" 20/05/09 Board Game Jungle speed / Arriba Review of the cool game "Jungle Speed" aka. "Arriba". 16/05/09 Danish Twin words "Twin words" are words that not only have multiple meanings, they must be composed next to each other in meaningful sentences. This article explores the concept of twin words. Nothing of interest? Try browsing the entire article archive... | |||||||||