25

I am looking for the fastest way to square a double (double d). So far I came up with two approaches:

1. d*d
2. Math.pow(d, 2)

To test the performance I set up three test cases, in each I generate random numbers using the same seed for the three cases and just calculate the squared number in a loop 100 000 000 times.

In the first test case numbers are generated using random.nextDouble(), in the second case using random.nextDouble()*Double.MAX_VALUE and in the third one using random.nextDouble()*Double.MIN_VALUE.

The results of a couple of runs (approximate results, theres always some variation, run using java 1.8, compiled for java 1.6 on Mac OSX Mavericks)

Approach | Case 1 | Case 2 | Case 3
---------•--------•--------•-------
    1    | ~2.16s | ~2.16s | ~2.16s
    2    | ~9s    | ~30s   | ~60s

The conclusion seems to be that approach 1 is way faster but also that Math.pow seems to behave kind of weird.

So I have two questions:

  1. Why is Math.pow so slow, and why does it cope badly with > 1 and even worse with < -1 numbers?

  2. Is there a way to improve the performance over what I suggested as approach 1? I was thinking about something like:

    long l = Double.doubleToRawLongBits(d);
    long sign = (l & (1 << 63));
    Double.longBitsToDouble((l<<1)&sign);
    

But that is a) wrong, and b) about the same speed as approach 1.

BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Samuel
  • 18,286
  • 18
  • 52
  • 88
  • I have no reference, but I thought I remember `Math.pow()` only being beneficial when using a larger exponent. If the simpler approach seems faster for squaring, why not just stick with it though? – Jesse Webb Aug 08 '14 at 14:33
  • 3
    Is `random.nextDouble()` inside your long loop? If so, you're measuring mostly that, not `d*d`. – Mike Dunlavey Aug 08 '14 at 15:15
  • 1
    @MikeDunlavey Of course, these numbers don't really mean much in absolute measures, but since generating the numbers is the same in all loops, you can kind of compare – Samuel Aug 08 '14 at 23:16

3 Answers3

15

The fastest way to square a number is to multiply it by itself.

Why is Math.pow so slow?

It's really not, but it is performing exponentiation instead of simple multiplication.

and why does it cope badly with > 1 and even worse with < -1 numbers

First, because it does the math. From the Javadoc it also contains tests for many corner cases. Finally, I would not rely too much on your micro-benchmark.

Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
8

Squaring by multipling with self is the fastest. Because that approch can be directly translated into simple, non-branching bytecode (and thus, indirectly, machine code).

Math.pow() is a quite complex function that comes with various guarantees for edge cases. And it need to be called instead of being inlined.

BuZZ-dEE
  • 6,075
  • 12
  • 66
  • 96
Durandal
  • 19,919
  • 4
  • 36
  • 70
2

Math.pow() is slow because it has to deal with the generic case or raising a number to any given power.
As for why it is slower with negative numbers, it is because it has to test if the power is positive or negative in order to give the sign, so it is one more operation to do.

Varpie
  • 57
  • 1
  • 8
  • I don't think one extra operation would result in that large of a discrepancy – radar Aug 08 '14 at 15:01
  • 3
    Given the javadoc, it tests many cases before testing if is negative, such as NaN, infinity for both the number and the power, etc. Source : [link](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/StrictMath.java#StrictMath.pow%28double%2Cdouble%29) – Varpie Aug 08 '14 at 15:12
  • + The person writing `Math.pow` has to assume some intelligence on the part of the user. Mostly it is used to raise positive numbers to arbitrary powers. Small integral powers are easily done by simple multiplication. – Mike Dunlavey Aug 08 '14 at 15:26