Calling DecimalFormat.format(double ...) when rounding mode is one of UP, DOWN, CEILING, FLOOR, UNNECESSARY will bring wrong rounding decision from DigitList.shouldRoundUp due to roumding-up/truncation of digits already happening in Double.toString() (more precisely sun.misc.FloatingDecimal.dtoa() called from toString()).
This will happen when dtoa() pre- rounding/truncation happens exactly at the DecimalFormat.maximumFractionDigits position
This is a long-standing bug (pre 9) that is an equivalent toJDK-7131459 for non HALF_* cases.
In that case threshold digit is not '5' (i.e. tie value) as in HALF_* modes, but '0' digit.
For example rounding at 2nd fractional digits position the input value "1.26" with a DecimalFormat.format()
call, using UP rounding mode, will WRONGLY provide 1.26 while it should provide 1.27 since best binary approximation is in fact a big greater (1.2600000000000000088817841970012523233890533447265625
) than 1.26.
In this case dtoa() truncated the result, thus suppressing the remaining digits that should be taken into account and which should cause rounding-up. DigitList.shouldRoundUp() should take into account pre- rounding-up/truncation from dtoa().
There 12 such errors for non HALF_* rounding modes. Below is the list of those :
// ***** Rounding errors in DigitList.ShouldRoundUp when non HALF_* rounding
// ***** happening when no more digits after the fractional rounding position
// ***** All these errors are due to sun.misc.FloatingDecimal.dtoa()
// ***** rounding-up/truncating the digits due to constraints of IEEE-754
// ***** dtoa() is called from Double.toString() call.
DecimalFormat corner cases rounding for NON HALF_* rules
DecimalFormat rounding at 2nd Fractional position
=============== UP rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (UP). Expected 1.27
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> UP rounding-mode similar on negative values (-1.1 --> -2) ...
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (UP). Expected -1.27
DecimalFormat.format() output for -1.26 -> -1.26
End =============== UP rounding mode "on threshold" cases ============== End
=============== DOWN rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (DOWN). Expected 1.23
DecimalFormat.format() output for 1.24 -> 1.24
--------------- Negative Values -------------
==> DOWN rounding-mode similar on negative values (-1.1 --> -1) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (DOWN). Expected -1.23
DecimalFormat.format() output for -1.24 -> -1.24
End =============== DOWN rounding mode "on threshold" cases ============== End
=============== CEILING rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (CEILING). Expected 1.27
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> CEILING rounding-mode different on negative values (-1.1 --> -1) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (CEILING). Expected -1.23
DecimalFormat.format() output for -1.24 -> -1.24
End =============== CEILING rounding mode "on threshold" cases ============== End
=============== FLOOR rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (FLOOR). Expected 1.23
DecimalFormat.format() output for 1.24 -> 1.24
--------------- Negative Values -------------
==> FLOOR rounding-mode different on negative values (-1.1 --> -2) ...
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (FLOOR). Expected -1.27
DecimalFormat.format() output for -1.26 -> -1.26
End =============== FLOOR rounding mode "on threshold" cases ============== End
=============== UNNECESSARY rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for 1.24 -> 1.24
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> UNNECESSARY rounding-mode similar on negative values (ArithmeticException thrown) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for -1.24 -> -1.24
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for -1.26 -> -1.26
End =============== UNNECESSARY rounding mode "on threshold" cases ============== End
This will happen when dtoa() pre- rounding/truncation happens exactly at the DecimalFormat.maximumFractionDigits position
This is a long-standing bug (pre 9) that is an equivalent to
In that case threshold digit is not '5' (i.e. tie value) as in HALF_* modes, but '0' digit.
For example rounding at 2nd fractional digits position the input value "1.26" with a DecimalFormat.format()
call, using UP rounding mode, will WRONGLY provide 1.26 while it should provide 1.27 since best binary approximation is in fact a big greater (1.2600000000000000088817841970012523233890533447265625
) than 1.26.
In this case dtoa() truncated the result, thus suppressing the remaining digits that should be taken into account and which should cause rounding-up. DigitList.shouldRoundUp() should take into account pre- rounding-up/truncation from dtoa().
There 12 such errors for non HALF_* rounding modes. Below is the list of those :
// ***** Rounding errors in DigitList.ShouldRoundUp when non HALF_* rounding
// ***** happening when no more digits after the fractional rounding position
// ***** All these errors are due to sun.misc.FloatingDecimal.dtoa()
// ***** rounding-up/truncating the digits due to constraints of IEEE-754
// ***** dtoa() is called from Double.toString() call.
DecimalFormat corner cases rounding for NON HALF_* rules
DecimalFormat rounding at 2nd Fractional position
=============== UP rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (UP). Expected 1.27
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> UP rounding-mode similar on negative values (-1.1 --> -2) ...
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (UP). Expected -1.27
DecimalFormat.format() output for -1.26 -> -1.26
End =============== UP rounding mode "on threshold" cases ============== End
=============== DOWN rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (DOWN). Expected 1.23
DecimalFormat.format() output for 1.24 -> 1.24
--------------- Negative Values -------------
==> DOWN rounding-mode similar on negative values (-1.1 --> -1) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (DOWN). Expected -1.23
DecimalFormat.format() output for -1.24 -> -1.24
End =============== DOWN rounding mode "on threshold" cases ============== End
=============== CEILING rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (CEILING). Expected 1.27
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> CEILING rounding-mode different on negative values (-1.1 --> -1) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (CEILING). Expected -1.23
DecimalFormat.format() output for -1.24 -> -1.24
End =============== CEILING rounding mode "on threshold" cases ============== End
=============== FLOOR rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD be rounded (FLOOR). Expected 1.23
DecimalFormat.format() output for 1.24 -> 1.24
--------------- Negative Values -------------
==> FLOOR rounding-mode different on negative values (-1.1 --> -2) ...
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD be rounded (FLOOR). Expected -1.27
DecimalFormat.format() output for -1.26 -> -1.26
End =============== FLOOR rounding mode "on threshold" cases ============== End
=============== UNNECESSARY rounding mode "on threshold" cases ==============
--------------- Positive Values -------------
Value below threshold at rounding position : 1.24
Default output for 1.24 -> 1.24
BigDecimal output for 1.24 -> 1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for 1.24 -> 1.24
Value above threshold at rounding position : 1.26
Default output for 1.26 -> 1.26
BigDecimal output for 1.26 -> 1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for 1.26 -> 1.26
--------------- Negative Values -------------
==> UNNECESSARY rounding-mode similar on negative values (ArithmeticException thrown) ...
Value below threshold at rounding position : -1.24
Default output for -1.24 -> -1.24
BigDecimal output for -1.24 -> -1.2399999999999999911182158029987476766109466552734375
Best binary approximation (abs value) below threshold : rounded-up.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for -1.24 -> -1.24
Value above threshold at rounding position : -1.26
Default output for -1.26 -> -1.26
BigDecimal output for -1.26 -> -1.2600000000000000088817841970012523233890533447265625
Best binary approximation (abs value) above threshold : truncated.
==> SHOULD throw ArithmeticException !
DecimalFormat.format() output for -1.26 -> -1.26
End =============== UNNECESSARY rounding mode "on threshold" cases ============== End
- relates to
-
JDK-8174722 Wrong behavior of DecimalFormat with RoundingMode.UP in special case
-
- In Progress
-
-
JDK-7131459 [Fmt-De] DecimalFormat produces wrong format() results when close to a tie
-
- Closed
-