JDK-8039915 : Wrong NumberFormat.format() HALF_UP rounding when last digit exactly at rounding position greater than 5
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.text
  • Affected Version: 8,9
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2014-04-09
  • Updated: 2019-05-15
  • Resolved: 2014-10-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8 JDK 9
8u40Fixed 9 b36Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) Server VM (build 25.0-b70, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux paris 2.6.32-220.4.1.el6.i686 #1 SMP Mon Feb 6 16:10:45 CET 2012 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
Formating 0.950000550000 to a six fraction digits works properly, I get "0.950001".
Formating 0.950000600000 to a six fraction digits works wrong, I get "0.95" instead of "0.950001".


REGRESSION.  Last worked in version 7u51


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
@Test
public void test_roundingOfDoubleValues() {
  NumberFormat format = DecimalFormat.getInstance( Locale.US );
  format.setGroupingUsed( false );
  format.setMaximumFractionDigits( 6 );
  format.setRoundingMode( RoundingMode.HALF_UP );
  String val95000055 = format.format( 0.950000550000 );
  assertEquals( "0.950001", val95000055 );  // this is okay
  String val95000060 = format.format( 0.950000600000 );
  assertEquals( "0.950001", val95000060 );  // this provides an error
}

---------- END SOURCE ----------


Comments
Review thread: http://mail.openjdk.java.net/pipermail/core-libs-dev/2014-September/028583.html
21-10-2014

Digging further the problem, I discovered this is in fact coming from the DigitList.ShouldRoundUp() method modified for dealing with 7131459 JDK long-standing bug we discovered with fastpath implementation, even if this does not use fastpath code. This is a rare rounding corner case that was nether tested by any of the existing test suites (jdk unit tests, sqe testbase, jck test suite). Thus I am reassigning it to myself.
10-09-2014

This is an rounding case where FloatingDecimal.dtoa() rounding behavior must be ignored. HALF_UP case should separate "exact tie" case and "above the tie" case, while currently merging it into "above or equal to tie" case. Additional unit tests to be writen.
10-09-2014

The rounding threshold is a 5 digit at "rounding position"+1 Note: For any binary representation of a floating-point value, the last digit of it, which in most case will the closest representable approximation, will be a '5' ! i.e. 1.26 cannot be represented exactly, closest approx is 1.2600000000000000088817841970012523233890533447265625. in addition IEEE-754 format imposes constraint on the possible precision, thus Double.toString (called in DigitList.set()) will return here 1.26. For every value, we have only 3 possibilities for pre- rounding/truncation by sun.misc.FloatingDecimal.dtoa() (called by Double.toString()). A- binary representation exact, we have all digits (no round-up/truncation, last digit is a 5) B- binary representation inexact and digit sequence was truncated C- binary representation inexact and digit sequence was rounded up We must check against the digit at the rounding position. The 6 different cases are thus : 1- digit > 5 ==> must round-up 2- digit < 5 ==> dont round-up 3- digit == 5 ==> 3-a '5' digit is not the last one (there are following further digits that are ignored) ==> round-up 3-b '5' digit is the last one : 3-b-1 exact representation ==> must round-up 3-b-2 digit sequence rounded-up by dtoa() ==> dont round-up a second time !!! 3-b-3 digit sequence truncated by dtoa() ==> must round-up (take into account remaining digits) Problem with current code is that it mixes case 1 and 3 above. So the code is ok when rounding at the last available digit, with a '5' digit, but wrong when this digit is greater than '5', like here with 0.9500006 at the sixth fractional digit. Solution: separate case 1 and 3 in the code.
03-09-2014

Unassigning myself since not being related to fastpath
22-04-2014

I believe this is yet another false claim for the rounding fix in 8.
09-04-2014