9

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.

Ben S
  • 68,394
  • 30
  • 171
  • 212
mrjbq7
  • 238
  • 3
  • 7
  • 2
    @TrueWill: If it's neatly in a function that's properly named... Would it really matter? Perhaps it's being used in a math-intensive program. – Sivvy Nov 17 '09 at 18:20
  • It would be rare that you would find a significantly faster way of doing something for a method that has been around since 1.0 while maintaining 100% consistency with the original method. – TofuBeer Nov 17 '09 at 18:32
  • Absolutely is a micro-optimization, and in a local sense was worth it. -- but these are not recommended for general use. – mrjbq7 Nov 17 '09 at 18:53
  • 3
    It's NOT a senseless optimization. I have an algorithm that depends heavily on `Math.round` and this helped me a lot. Thanks! – Nowaker Sep 30 '11 at 11:50

3 Answers3

15

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.
Kristian
  • 6,357
  • 4
  • 36
  • 37
6

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.

Community
  • 1
  • 1
Tim Gage
  • 1,381
  • 10
  • 19
5

Yes; you're not accounting for underflow or overflow. Pragmatically speaking, this may not matter for your application.

Jonathan Feinberg
  • 44,698
  • 7
  • 80
  • 103