HiveBrain v1.2.0
Get Started
← Back to all entries
debugjavaMinor

Java helper function to round a number to the specified number of decimal places

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
placesnumberthehelperdecimalfunctionjavaroundspecified

Problem

The function I need:


Rounds a double to the specified number of decimal places

I figured this would be a part of java.lang.math, but inexplicably it doesn't have a round function that returns a double. Thus, my proposed solution is to add my own round function in a MathEx class.

/**
 * Holds generic math functions to extend the capabilities of the java.lang.Math class
 */
public class MathEx {

    /**
     * Rounds a double to the specified number of decimal places using the ubiquitous "half up" rounding method
     * @param numberToRound The number to be rounded
     * @param numberOfDecimals The number of decimals to include in the rounded number
     * @return The rounded number
     */
    public static double round(double numberToRound, int numberOfDecimals) {

        return new BigDecimal(numberToRound)
                    .setScale(numberOfDecimals, BigDecimal.ROUND_HALF_UP)
                    .doubleValue();
    }
}


I'd appreciate feedback on the overall strategy, the Javadoc comments, and the naming conventions.

Solution

This question makes assumptions which are not supported by the actual implementations of double in Java (or most other languages).

double is a floating-point number format, which has a number of odd behaviours that simply do not support 'rounding' in a consistently accurate way.

Consider the following code:

double d = (v * 10 + 4) / 10_000_000.0;
    double rescale = round(d, 6) * 1_000_000_000.0;


That code takes an input value v, multiplies it by 10, adds 4, and then divides it by 10 million.

Consider an input value 65. Take that, multiply by 10, and add 4, to get 654. Now, divide 654 by 10 million, to get 0.0000654.

Now, round that value to 6 decimals, and get: 0.0000650, right?

Then multiply that by 1 billion to get 65000

Unfortunately, the actual result is:

64999.999999999990000000


The reason is that 0.000065 does not exist. Sure, many things that print numbers in Java will print a rounded value for you, but any math you do will have small errors inconsistent with your expectations.

The bottom line?

Rounding floating-point numbers to any fractional value will result in occasional errors.

The solution is to keep everything as a BigDecimal number. Do not use your method unless you know what you are sacrificing.

Here is an ideone, it loops through small values until it finds one which causes errors. The first error it finds is at 65 millionths.

Code Snippets

double d = (v * 10 + 4) / 10_000_000.0;
    double rescale = round(d, 6) * 1_000_000_000.0;
64999.999999999990000000

Context

StackExchange Code Review Q#85087, answer score: 3

Revisions (0)

No revisions yet.