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.
–
–
@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.