Repository navigation
Conversation
…ned digits When round() runs with roundExponent == max, digitCount == 1 and the single digit equals ROUND_CENTER (5), shouldRoundUp(0) evaluates digits[count - 1] with count == 0, throwing ArrayIndexOutOfBoundsException: Index -1. Guard the half-even tie-break with count > 0 so a zero-retained-digit tie resolves to zero (the implicit retained digit is 0, which is even) instead of reading digits[-1]. Values with a single digit greater than 5 still round up correctly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
When
round()runs withroundExponent == max,digitCount == 1and the single digit equalsROUND_CENTER(5),shouldRoundUp(0)evaluatesdigits[count - 1]withcount == 0, throwingArrayIndexOutOfBoundsException: Index -1.This PR guards the half-even tie-break with
count > 0so a zero-retained-digit tie resolves to zero (the implicit retained digit is 0, which is even) instead of readingdigits[-1]. Values with a single digit greater than 5 still round up correctly.Root cause
ParsedDecimal.round()(line 546) callsshouldRoundUp(0)in theroundExponent == maxbranch:shouldRoundUp(int count)(lines 634-635) evaluates:When
count == 0anddigitCount == 1:digitAfterLast = digits[0] = 5 = ROUND_CENTER, sodigitAfterLast > ROUND_CENTERis false.digitAfterLast == ROUND_CENTERis true, so the right-hand side is evaluated.count < digitCount - 1is0 < 0= false.digits[count - 1]=digits[-1]->ArrayIndexOutOfBoundsException.The trigger is a value of the form
±5 x 10^kwithminDecimalExponent = k+1, i.e. a single significant digit of exactly 5 with all significant digits rounded away (a half-even tie with zero retained digits).Builder.minDecimalExponent(int)documents no constraint on its argument and explicitly promises that "If the number being formatted does not contain any such digits, then zero is returned" — rounding all significant digits away is a documented, supported scenario, so the AIOOBE violates the documented contract.Fix
Add a
count > 0guard to the half-even tie-break inshouldRoundUp:When
count == 0anddigitCount == 1, the tie now resolves to zero (the implicit retained digit is 0, which is even) instead of readingdigits[-1]. A single digit greater than 5 still rounds up via thedigitAfterLast > ROUND_CENTERbranch, and thecount < digitCount - 1short-circuit fordigitCount > 1is unchanged.Note: an alternative fix of gating
round()withdigitCount > 1 && shouldRoundUp(0)was considered and rejected, because it would also send single-digit values greater than 5 (e.g.0.7rounded to the units place, which should produce1) to the zero branch.Reproducer (fails without this fix)
With this fix, each call returns normally (the tie resolves to zero).