3

Am I doing something terribly wrong or does DecimalFormat really have an issue with rounding?

DecimalFormat format = new DecimalFormat("#.####");
format.setRoundingMode(RoundingMode.HALF_UP); // .5 up, .4 down?
format.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ENGLISH)); // . as decimal seperator

System.out.println(format.format(1.848751)); // 1.8488 -> thats correct
System.out.println(format.format(1.848750)); // 1.8487 -> that is not correct
System.out.println(format.format(1.84875)); // 1.8487 -> that is not correct
  • 2
    Interesting question, I guess you could take a look at this: https://stackoverflow.com/questions/42423366/odd-result-from-decimalformat-rounding-half-up – George Mar 31 '22 at 12:06
  • Err, you are passing floating point values to the formatter, so you are subject to floating point representation issues. Try to use a solution that takes your numbers as String for example. Meaning: maybe you get different results because the *actual* value that the formatter works on isn't what you expect it is! – GhostCat Mar 31 '22 at 12:13
  • I don't think precision is my issue. Also happens if I convert it to double (Actually the real use cases is double, I just stripped it down a little too far for the question here ^^) I did some debugging and I think its an off-by-one error in java.text.DigitList#shouldRoundUp – flitzpiepe96 Mar 31 '22 at 12:43
  • 1
    The problem happens before `shouldRoundUp`. The method `FloatingDecimal.BinaryToASCIIBuffer.dtoa(…)` produces the sequence `1.84874`, followed by rounding it up to `1.84875` and telling the caller that the value was rounded up already, so `shouldRoundUp` returns `false` because `alreadyRounded` is `true`. – Holger Mar 31 '22 at 15:14
  • 1
    By the way, it’s [not the first time there’s a problem with `RoundingMode.HALF_UP`](https://stackoverflow.com/q/24426438/2711488) but this is a different issue. – Holger Mar 31 '22 at 15:17
  • Those are two different ways of rounding. The rounding that The ASCIIConverter does is to mitigate floating point inaccuracy isn't it? The alreadyRounded flag should (in my opinion) never come into effect. The pure fact that we hit the else branch in DigitList.shouldRoundUp that uses this flag is already an issue I think. The comment in that block says "Digit at rounding position is the last one", which is a lie. There is one additional digit after the rounding position that is plain ignored – flitzpiepe96 Apr 01 '22 at 13:16
  • 2
    “floating point inaccuracy”—that’s the problem. When you assign `1.84875` to a `double`, the actual value stored in the variable is `1.84874999999999989341858963598497211933135986328125`. Which effectively gets rounded down, which is correct for this value. There is no way for the formatter, to distinguish between `format.format(1.84875)` and `format.format(1.8487499999999997824)` (or any value between these two). When you use `format.format(BigDecimal.valueOf(1.84875))`, you get the intended behavior. It behaves like rounding to `1.84875`, without considering it as rounded when formatting. – Holger Apr 05 '22 at 08:53

0 Answers0