<i18n dev> RFR: 8369050: DecimalFormat Rounding Errors for Ties Torwards 1 in LSD or 0
Justin Lu
jlu at openjdk.org
Tue Oct 7 23:52:39 UTC 2025
On Tue, 7 Oct 2025 23:19:28 GMT, Justin Lu <jlu at openjdk.org> wrote:
> Please review this PR which corrects a rounding error for DecimalFormat on tie cases when the maximum fraction digits allowed is one less than the position of the first significant digit in the double rounded. For example, rounding a value such as 0.0005 to a maximum of 3 fractional digits. In order to get the correct `count` value, we must strip the trailing zeros prior to the check to ensure we have the correct value of `count` = 1 so that we can go down the _Rounding position is the last index_ case which properly checks if the String double was already rounded or is exact from its true decimal form.
For some history, this bug is long-standing, but has taken different forms after recent changes to DigitList.
DigitList, when handling the specific case as mentioned in the summary statement returns a faulty value due to an incorrect value of `count`. DigitList used to parse its own `count` value, but was recently changed in [JDK-8367324](https://bugs.openjdk.org/browse/JDK-8367324) to retrieve `count`from `FloatingDecimal`.
Consider the double `0.00005` rounded to 4 fractional digits, `count` prior to JDK-8367324 would be calculated as 2 while it is now calculated as 17 due to the implementation differences.
Thus, when handling the following case,
} else if (-decimalAt == maximumDigits) {
// If we round 0.0009 to 3 fractional digits, then we have to
// create a new one digit in the least significant location.
if (shouldRoundUp(0, roundedUp, valueExactAsDecimal)) {
count = 1;
++decimalAt;
digits[0] = '1';
} else {
For both `count` values, `if (maximumDigits == (count - 1))` is erroneously evaluated in `shouldRoundUp` under the HALF_* cases and instead falls under the _Rounding position is not the last index_ case.
Notice that before JDK-8367324, `count` was still being read **incorrectly** as 2 (for fractional doubles in exponential form). However, the behavioral differences had not been observed, since the HALF_UP/DOWN cases prior to https://bugs.openjdk.org/browse/JDK-8314169 could return the right values by chance when going down the wrong path.
test/jdk/java/text/Format/DecimalFormat/RoundingToLSDTieTest.java line 1:
> 1: /*
I considered adding the tests in this file under _TieRoundingTest_, but did not do so to make review easier since JUnit could be used for the test in this PR. (_TieRoundingTest_ is old and does not use JUnit.) Will consider updating _TieRoundingTest_ to use JUnit and merging the tests in this PR under it in a separate issue.
test/jdk/java/text/Format/NumberFormat/NumberRegression.java line 1779:
> 1777: "0%", "0%", "1%", "1%", "1%",
> 1778: "0", "2", "0.2", "0.6", "0.04",
> 1779: "0.04", "0.001", "0.002",
This test is checking that `.0005` rounded to 3 digits is "0.000" under HALF_EVEN.
This is wrong. See,
`new BigDecimal(0.0005) ==> 0.0005000000000000000104083408558608425664715468883514404296875`.
Resolving the bug in this PR requires fixing this incorrect test case. From the JDK-4241880 issue, it seems this case may have been put in to "document the incorrect but actual behavior".
-------------
PR Comment: https://git.openjdk.org/jdk/pull/27681#issuecomment-3379027299
PR Review Comment: https://git.openjdk.org/jdk/pull/27681#discussion_r2412168905
PR Review Comment: https://git.openjdk.org/jdk/pull/27681#discussion_r2412167812
More information about the i18n-dev
mailing list