DecimalFormat regression bug in Java 8?

Olivier Lagneau olivier.lagneau at oracle.com
Tue Oct 22 14:18:12 UTC 2013


Hello Lennart,

Sorry for the little belated reply.

The behaviour you see is due to a fix for JDK-7131459 and is the 
expected one.

While working on the DecimalFormat optimization which you noticed in the 
code, we discovered a long-standing bug in the DigitList.java class
(see bug 7131459 <http://bugs.sun.com/view_bug.do?bug_id=7131459>).
We have fixed this bug and the fix is now part of the JDK8 builds 
(starting in b82 IIRC). so b108 and b111 are fixed.

The fact is that most decimal values cannot be represented exactly in a 
binary representation.
For example the closest binary representation to -0.15 is 
-0.1499999999999999944488848768742172978818416595458984375 .

As you can see the absolute value of this representation is "below" the 
tie value "0.15".
So the closest binary representation of -0.15 that can be recorded in a 
computer  is a bit greater than the "tie" value provided in the program 
text ("-0.15"),  and for this reason, as stated by the HALF-EVEN 
rounding rules, we should not round the result, in order to provide an exact
and fair HALF-EVEN rounding of *what is recorded* in the computer.

Thus the correct result to return here is -0.1 because 
maximumFractionDigits is set to 1, and correct rounding leaves the '1' 
digit in 1st
fractional position unchanged.

Approximated closest binary representation is the case of all values you 
provided in your example, except for 0.25 which can be represented 
*exactly*. So there is no change in the output only for that value when 
comparing older and recent builds.

The correct formatting is the one provided by recent JDK8 builds like 
b108 and b111.

To help understanding, I have changed a bit your example to show what is 
the approximated binary representation that is recorded
in the computer memory by using the call "new BigDecimal(d).toString()" 
which provides the exact decimal representation of what is
recorded in the computer :

you will find below the changed code and the corresponding results for 
JDK8 builds that include (or don't include) the fix for
JDK-7131459

Hope that helps.

Best Regards,
Olivier.

changed code :
--------------------------------------
import java.text.DecimalFormat;
import java.math.BigDecimal;

public class Jdk8Formatting {

     public static void main(String[] args) {

         double dd[] = {-0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 
0.55, 0.65};
         DecimalFormat fmt = new DecimalFormat("0.#");

         for (double d : dd) {
             // Prints-out first the "approximated" double 
floating-point value
             System.out.println("FloatingDecimal output : " + d);

             // Then the exact binary representation using as much 
memory as needed
             System.out.println("BigDecimal      output : " + new 
BigDecimal(d).toString());

             // And finally the string returned by DecimalFormat.format()
             System.out.println(d + " " + fmt.format(d));
         }
     }
}
--------------------------------------

Output of the above program for JDK8 builds that include the fix:
FloatingDecimal output : -0.15
BigDecimal      output : 
-0.1499999999999999944488848768742172978818416595458984375
-0.15 -0.1
FloatingDecimal output : -0.05
BigDecimal      output : 
-0.05000000000000000277555756156289135105907917022705078125
-0.05 -0.1
FloatingDecimal output : 0.05
BigDecimal      output : 
0.05000000000000000277555756156289135105907917022705078125
0.05 0.1
FloatingDecimal output : 0.15
BigDecimal      output : 
0.1499999999999999944488848768742172978818416595458984375
0.15 0.1
FloatingDecimal output : 0.25
BigDecimal      output : 0.25
0.25 0.2
FloatingDecimal output : 0.35
BigDecimal      output : 
0.34999999999999997779553950749686919152736663818359375
0.35 0.3
FloatingDecimal output : 0.45
BigDecimal      output : 
0.450000000000000011102230246251565404236316680908203125
0.45 0.5
FloatingDecimal output : 0.55
BigDecimal      output : 
0.5500000000000000444089209850062616169452667236328125
0.55 0.6
FloatingDecimal output : 0.65
BigDecimal      output : 
0.65000000000000002220446049250313080847263336181640625
0.65 0.7

---------------------------------------------------------------------------------

Output of the above program for JDK8 builds that *do not* include the fix:
FloatingDecimal output : -0.15
BigDecimal      output : 
-0.1499999999999999944488848768742172978818416595458984375
-0.15 -0.2
FloatingDecimal output : -0.05
BigDecimal      output : 
-0.05000000000000000277555756156289135105907917022705078125
-0.05 -0
FloatingDecimal output : 0.05
BigDecimal      output : 
0.05000000000000000277555756156289135105907917022705078125
0.05 0
FloatingDecimal output : 0.15
BigDecimal      output : 
0.1499999999999999944488848768742172978818416595458984375
0.15 0.2
FloatingDecimal output : 0.25
BigDecimal      output : 0.25
0.25 0.2
FloatingDecimal output : 0.35
BigDecimal      output : 
0.34999999999999997779553950749686919152736663818359375
0.35 0.4
FloatingDecimal output : 0.45
BigDecimal      output : 
0.450000000000000011102230246251565404236316680908203125
0.45 0.4
FloatingDecimal output : 0.55
BigDecimal      output : 
0.5500000000000000444089209850062616169452667236328125
0.55 0.6
FloatingDecimal output : 0.65
BigDecimal      output : 
0.65000000000000002220446049250313080847263336181640625
0.65 0.6
---------------------------------------------------------------------------



Lennart Börjeson said  on date 10/21/2013 11:26 AM:
> I've found what I believe is a DecimalFormat regression bug in Java 8, i.e. Java 8 seems to format some numbers differently than previous releases do.
>
> I've tried submitting a proper bug report using the form at bugreport.sun.com, but it just throws back a "submission error" at me, so I resort to mailing you.
>
> Just try the code below.
> --------------
> import java.text.DecimalFormat;
> public class Jdk8FormattingBug {
>
>     public static void main(String[] args) {
>         double dd[] = {-0.15, -0.05, 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65};
>      DecimalFormat fmt = new DecimalFormat("0.#");
>
>      for (double d : dd) {
>      System.out.println(d+" "+fmt.format(d));
>      }
>      }
> }
> ----------------
>
> I've tested JDK 1.8 b108 and b111, on Linux and on Mac, and they all print:
>
> -0.15 –0.1
> -0.05 –0.1
> 0.05 0.1
> 0.15 0.1
> 0.25 0.2
> 0.35 0.3
> 0.45 0.5
> 0.55 0.6
> 0.65 0.7
>
>
> Yet the printout using all other Java versions I've tried is:
>
> -0.15 –0.2
> -0.05 -0
> 0.05 0
> 0.15 0.2
> 0.25 0.2
> 0.35 0.4
> 0.45 0.4
> 0.55 0.6
> 0.65 0.6
>
> which is the documented behaviour, respecting RoundingMode.HALF_EVEN.
>
> I'd expect formatting to behave the same in Java 8, or have I missed some change here? I've noticed there has been some optimisation work done on DecimalFormat, but shouldn't the output stay the same?
>
> Best regards,
>
> Lennart Börjeson




More information about the core-libs-dev mailing list