Inaccurate rounding with decimal digits

I made a small post about Inexact rounding up or down with decimal digits. But it is also possible to run into the same bug when rounding floating point numbers to the next number with restricted decimal digits. A really common example is the price calculation. You have the price 1.99 for example and the tax is maybe 19%. The resulting price with tax would be 2.3681. So you will need to round the price with two decimal digits to 2.37.

Rounding with two decimal digits

A similar scenario as in the mentioned post from above. I will assume that we want to round the number 0.145 with two decimal digits. The expected result should be 0.15, cause we round half up.

Naive implementation

Here some naive implementations that I have done myself in the past and that are pretty often used in some common programming languages.

Java

The naive Java implementation looks like this.

public static double naiveRound2Digits(double number) {
    return Math.round(number * 100) / 100.0;
}

The output of the following call is 0.14 instead of 0.15.

System.out.println(naiveRound2Digits(0.145));

JavaScript

The same naive function in JavaScript.

function naiveRound2Digits( number ) {
    return Math.round( number * 100 ) / 100;
};

The same wrong result 0.14.

console.log( naiveRound2Digits( 0.145 ) );

Python

The naive implementation in Python.

def naiveRound2Digits( number ):
    return round( number * 100 ) / 100;

Again the wrong result 0.14.

print naiveRound2Digits( 0.145 );

C

Same procedure as in the other post that I mentioned above, I will give a last naive example in the programming langiage C just to make clear this problem has nothing incommon with the choice of the programming language.

double naiveRound2Digits(double number) {
    return round(number * 100) / 100;
}

Here the same wrong result 0.140000.

printf("%f\n", naiveRound2Digits(0.145));

Naive problem

All naive implementations have the same procedure with the following three steps.

  • Multiply with 100
  • Round to the closed integer value
  • Divide by 100

Our example input 0.145 has the following behavior.

  • 0.145 * 100 = 14.499999999999998
  • round(14.499999999999998) = 14
  • 14 / 100 = 0.14

Step 1 has one small error of 0.000000000000002 that is normal for floating point numbers, but step 2 is increasing our error now. Instead of rounding up on the half, we are rounding down.

Better implementation

I wil show now some better implementations for all four programming languages that are mentioned above.

Java

The implementation in Java.

public static double round2Digits(double number) {
    return Math.round(Math.round(number * 1000) / 10.0) / 100.0;
}

With the right result 0.15.

System.out.println(round2Digits(0.145));

JavaScript

The implementation in JavaScript.

function round2Digits( number ) {
    return Math.round( Math.round( number * 1000 ) / 10 ) / 100;
};

As expected the right result 0.15.

console.log( round2Digits( 0.145 ) );

Python

The implementation in Python.

def round2Digits( number ):
    return round( round( number * 1000 ) / 10 ) / 100;

The same right result 0.15.

print round2Digits( 0.145 );

C

The implementation in C.

double round2Digits(double number) {
    return round(round(number * 1000) / 10) / 100;
}

Again the same exact result 0.150000.

printf("%f\n", round2Digits(0.145));

Exact solution

The inexact claculation or rounding from above is really a problem has is known and common for floating point numbers. Here are the steps from the more exact soltions.

  • Multiply by 1000
  • Round to the closed integer value
  • Divide by 10
  • Round to the closed integer value
  • Divide by 100
Next Previous