Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I'm having trouble with rounding. Specifically, after reading all the javadoc, I was expecting the following code:

int n = (integer between 0 and 9, included)
new BigDecimal(n + 0.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()

to return n + 0.56. Instead, these are the return values for n from 0 to 4:

 new BigDecimal(0.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 new BigDecimal(1.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 new BigDecimal(2.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 new BigDecimal(3.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 new BigDecimal(4.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()

I have also tried to change the rounding mode:

int n = (integer between 0 and 9, included)
new BigDecimal(n + 0.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()

expecting n + 0.55 as a result for each and every n. Instead, the return values are exactly the same as the previous example:

 new BigDecimal(0.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 new BigDecimal(1.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 new BigDecimal(2.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 new BigDecimal(3.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 new BigDecimal(4.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()

Am I missing something?

The problem you have is that double is not a precise representation and you are round based on this imprecise number.

BigDecimal bd = new BigDecimal(1.555d);
System.out.println("bd=" + bd);
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println("after rounding bd=" + bd);
double d = bd.doubleValue();
System.out.println("after rounding d=" + d);

prints

bd=1.5549999999999999378275106209912337362766265869140625
after rounding bd=1.55
after rounding d=1.55

however

BigDecimal bd = BigDecimal.valueOf(1.555d);
System.out.println("bd=" + bd);
bd = bd.setScale(2, RoundingMode.HALF_UP);
System.out.println("after rounding bd=" + bd);
double d = bd.doubleValue();
System.out.println("after rounding d=" + d);

prints

bd=1.555
after rounding bd=1.56
after rounding d=1.56

This works because BigDecimal.valueOf does some extra rounding based on how double would appear if you printed it.

However I wouldn't use BigDecimal unless performance/simplicity is not an issue.

double d = 1.555d;
System.out.println("d=" + d);
d = roundToTwoPlaces(d);
System.out.println("after rounding d=" + d);
public static double roundToTwoPlaces(double d) {
    return ((long) (d < 0 ? d * 100 - 0.5 : d * 100 + 0.5)) / 100.0;

prints

d=1.555
after rounding d=1.56

For more details Double your money again compares the performance of different ways of rounding.

Thank you very much, I was sure I was forgetting about something but couldn't quite point out the problem. – ilCatania Aug 19, 2011 at 16:34 I was running my unit tests and it wasn't making sense the values returned with the double rounding half up. But with BigDecimal everything start working. – Manu Nov 7, 2018 at 1:07

@Peter Lawrey I looked at your examples and wrote a quick program comparing the simple approach that you posted along with all the methods for RoundingMode. The code is here for anyone interested, this will clearly show the differences:

[RoundingMode.java] https://gitlab.com/bobby.estey/java/-/blob/master/maven/jdk14/src/main/java/mathematics/RoundingModeExamples.java

Results:
lowDouble:                             1.55553
simple - lowDouble:                    1.5555
RoundingMode.UP - lowDouble:           1.5556
RoundingMode.DOWN - lowDouble:         1.5555
RoundingMode.CEILING - lowDouble:      1.5556
RoundingMode.FLOOR - lowDouble:        1.5555
RoundingMode.HALF_UP - lowDouble:      1.5555
RoundingMode.HALF_DOWN - lowDouble:    1.5555
RoundingMode.HALF_EVEN - lowDouble:    1.5555
highDouble:                            1.55555
simple - highDouble:                   1.5556
RoundingMode.UP - highDouble:          1.5556
RoundingMode.DOWN - highDouble:        1.5555
RoundingMode.CEILING - highDouble:     1.5556
RoundingMode.FLOOR - highDouble:       1.5555
RoundingMode.HALF_UP - highDouble:     1.5555
RoundingMode.HALF_DOWN - highDouble:   1.5555
RoundingMode.HALF_EVEN - highDouble:   1.5555
double DecimalValue = 3.1452;
BigDecimal decimal = new BigDecimal(DecimalValue).setScale(2, RoundingMode.DOWN);
 RoundingMode.CEILING

Rounding mode to round towards positive infinity. For positive values this rounding mode behaves as UP, for negative values as DOWN. Rule: x.round() >= x

RoundingMode.DOWN

Rounding mode where the values are rounded towards zero. Rule: x.round().abs() <= x.abs()

RoundingMode.DOWN

Rounding mode to round towards negative infinity. For positive values this rounding mode behaves as DOWN, for negative values as UP. Rule: x.round() <= x

RoundingMode.HALF_DOWN

Rounding mode where values are rounded towards the nearest neighbor. Ties are broken by rounding down.

RoundingMode.HALF_EVEN

Rounding mode where values are rounded towards the nearest neighbor. Ties are broken by rounding to the even neighbor.

RoundingMode.HALF_UP

Rounding mode where values are rounded towards the nearest neighbor. Ties are broken by rounding up.

 RoundingMode.UNNECESSARY

Rounding mode where the rounding operations throws an ArithmeticException for the case that rounding is necessary, i.e. for the case that the value cannot be represented exactly.

RoundingMode.UP

Rounding mode where positive values are rounded towards positive infinity and negative values towards negative infinity. Rule: x.round().abs() >= x.abs()

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.