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