Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Faster implementation of Math.round?

Are there any drawbacks to this code, which appears to be a faster (and correct) version of java.lang.Math.round?

public static long round(double d) {

    if (d > 0) {
        return (long) (d + 0.5d);
    } else {
        return (long) (d - 0.5d);
    }
}

It takes advantage of the fact that, in Java, truncating to long rounds in to zero.

like image 269
mrjbq7 Avatar asked Nov 17 '09 18:11

mrjbq7


2 Answers

There are some special cases which the built in method handles, which your code does not handle. From the documentation:

  • If the argument is NaN, the result is 0.
  • If the argument is negative infinity or any value less than or equal to the value of Integer.MIN_VALUE, the result is equal to the value of Integer.MIN_VALUE.
  • If the argument is positive infinity or any value greater than or equal to the value of Integer.MAX_VALUE, the result is equal to the value of Integer.MAX_VALUE.
like image 120
Kristian Avatar answered Oct 26 '22 14:10

Kristian


I've been testing this, and there is one key potential drawback which has not yet been described here: You are changing the rounding tie-breaking method.

Math.round() implements the "round half up" rule, whereas your round() method implements the "round half away from zero" rule.

For example:

  • Math.round(-0.5d) => 0L
  • Your.round(-0.5d) => -1L

This may or may not be a problem for you, but you should understand that the above method is not a drop-in replacement for Math.round(), even after the NaN and infinity considerations already outlined.

Another relevant question: Rounding negative numbers in Java

As for the performance, there is no doubt that the above method is significantly faster than Math.round() - it runs in about 35% of the time for randomly generated positive and negative values. This can be a worthwhile optimisation when calling this method in a tight loop. It's even better (25% of the runtime) when given only positive values, possibly because of the CPU using branch prediction.

Math.round() is ultimately implemented by a native JNI call, which might be the cause of the performance difference. This Sun/Oracle bug suggests there might be pure-Java version in j6u22, but I can't see where, and indeed Math.round() in j6u23 performs similarly to j6u16 in my tests. I have not tested on other versions.

like image 28
Tim Gage Avatar answered Oct 26 '22 14:10

Tim Gage