JDK-8227429 : Incorrect rounding with String.format()
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 8,11,13
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2019-06-28
  • Updated: 2024-05-02
  • Resolved: 2024-05-02
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
OSX, JDK 12,8

A DESCRIPTION OF THE PROBLEM :
Wrong rounding in formatter:
      double v3 = 0.00024649999999999997d;
       System.out.println(String.format("%.6f", v3));
       System.out.println(String.format("%.7", v3));
0.000247 (incorrect)
0.0002465

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
double v3 = 0.00024649999999999997d;
       System.out.println(String.format("%.6f", v3));
       System.out.println(String.format("%.7f", v3));

double v2 = .0002755d;
System.out.println(new DecimalFormat("#.######").format(v2));

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
0.000246
ACTUAL -
0.000247

---------- BEGIN SOURCE ----------
double v3 = 0.00024649999999999997d;
System.out.println(String.format("%.6f", v3));
System.out.println(String.format("%.7f", v3));
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
BigDecimal maybe, but it's slow

FREQUENCY : always



Comments
As explained in a comment, although surprising the observed behavior matches the specification.
02-05-2024

Interesting analysis. So it seems that the implementation is conforming to the specification; but could the specification be improved, or at least changed to avoid the two rounding steps? On the one hand, it might be nice to expose an arbitrary precision floating point to decimal conversion. (As noted by the submitter, one can use BigDecimal for this.) On the other hand, the current behavior at least makes sense in some cases. In particular this case, where the literal 0.0002465 is a decimal in source code (but is represented as a double internally) and formatting as a decimal using Formatter gives a sensible result. Maybe changing this isn't worth it.
16-05-2022

The examples in the bug report are not an issue. For example, let double d = 0.0002465. As usual, this really stands for the double closest to the decimal literal x = 0.0002465 (x is meant to be a real number, not a double), so d is slightly different than x. In fact, as mentioned above, the full decimal expansion of d is 0.00024649999999999997433997034335106945945881307125091552734375 which is slightly less than x. So, if this very long decimal were to be rounded to 6 fractional digits directly, it would show as 0.000246. Problem is, Formatter specifies that it first converts a double by Double.toString() obtaining a decimal and then, if needed, rounds that decimal. In this case, d is first converted to the decimal x = 0.0002465, which is the shortest decimal closest to d (as by spec of toString()). Then x is rounded to 6 fractional digits. Since x lies exactly between 0.000246 and 0.000247, and since rounding in Formatter is HALF_UP, the result is 0.000247. While the result might be surprising, it meets the spec. In fact, the spec, perhaps unknowingly, allow the phenomenon of double roundings (in the sense of "two times", "twice", https://en.wikipedia.org/wiki/Rounding#Double_rounding), which is unfortunate. Another consequence of the spec of Formatter, is that asking for a rounding to 22 fractional digits would not return 0.0002464999999999999743 as could be expected from the full decimal expansion of d above, but instead returns 0.0002465000000000000000 because of the intermediary toString() step
16-05-2022

Circling back on this. It seems like there's a rounding bug inside of FloatingDecimal `java.base/jdk/internal/math/FloatingDecimal.java` that's sourcing the bad values in question. The rounding up bug occurs around 744-781. Will drill down on this.
08-04-2021

The exact numerical value of a double can be seen by converting the number to BigDecimal: jshell> BigDecimal bd = new BigDecimal(0.00024649999999999997d) bd ==> 0.00024649999999999997433997034335106945945881307125091552734375 jshell> BigDecimal bd2 = new BigDecimal(.0002755d) bd2 ==> 0.0002754999999999999745099732439967965547111816704273223876953125 so as noted in an earlier comment, the correct leading six digits of the first value under a round-to-nearest rounding mode are 0.000246
07-04-2021

Not a bug in the formatter.
07-04-2021

This does not appear to be a bug in the formatter. The value for the doubles given in the example are rounded to the given values by the process that is parsing the literal, not the formatter. Try the following in JShell: jshell> String.format("%.15f",Double.parseDouble("0.00024649999999999997d")); $5 ==> "0.000246500000000" Given a the format string "%.6f", the Formatter is using Rounding.HALF_UP correctly and showing 0.000247. Likewise "%.7f" is showing 0.0002465 correctly as well, because that's the value of the double that it receives. Propose closing this as not a bug.
07-04-2021

Not related to JDK-8262744
06-04-2021

Not sure whether this is closer to [~naoto] or [~bpb] ....
12-07-2019

Possibly related to changes done for JDK-7131459.
09-07-2019

The double value 0.00024649999999999997 is represented in system as 0.00024649999999999997433997034335106945945881307125091552734375 , so the String.format() should have used Rounding.HALF_UP, according to which for 6 digits it should have been 0.000246 as output. But instead it is 0.000247 as output. If the double value 0.00024649999999999997 is cast as float , it is represented in system as 0.000246500014327466487884521484375 and then the Rounding for both 6 and 7 fraction digits seems to give the output we are getting now.
09-07-2019