简体   繁体   中英

how does java.math.RoundingMode work?

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()
 0.56
 new BigDecimal(1.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 1.55
 new BigDecimal(2.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 2.56
 new BigDecimal(3.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 3.56
 new BigDecimal(4.555d).setScale(2, RoundingMode.HALF_UP).doubleValue()
 4.55

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()
 0.56
 new BigDecimal(1.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 1.55
 new BigDecimal(2.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 2.56
 new BigDecimal(3.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 3.56
 new BigDecimal(4.555d).setScale(2, RoundingMode.HALF_DOWN).doubleValue()
 4.55

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.

@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

0.555d is a double value. It may be slightly larger than 0.555 or slightly smaller.

Rounding mode use for Round the Decimal Value.

You can use with.

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, ie 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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM