Repository navigation
Conversation
A decimal built from a number or a string with a nonzero value whose magnitude is below 0.1, such as `decimal -0.0836` or `Decimal.Parse "-0.0836"`, kept the zeros after the decimal point at the start of its digits. Its stored exponent therefore differed from the normalized exponent of the copy that round() rounds, so Math.Floor, Math.Ceiling and Math.Round rounded at the wrong decimal place: Math.Floor(decimal -0.0836) returned -0.1 instead of -1, and Math.Round(decimal 0.0836, 2) returned 0.084 instead of 0.08. GetHashCode also differed from that of an equal decimal produced by arithmetic, which broke HashSet and Dictionary lookups. Strip those leading zeros in parse() when the value has a nonzero digit. This gives these values the normalized form that copies and arithmetic results already have. Zero keeps them, to preserve its scale (0.00). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
A decimal built from a number or a string with a nonzero value whose magnitude is below 0.1 rounds at the wrong place:
Math.Floor(decimal -0.0836)Math.Floor(Decimal.Parse "-0.0000123")Math.Ceiling(decimal 0.0836)Math.Round(decimal 0.06)Math.Round(decimal 0.0836, 2)Decimal literals and arithmetic results aren't affected:
Math.Floor(0M - 0.0836M)is -1.Cause
parse()inlib/big.jsstrips leading zeros only before the decimal point, so that zero keeps its scale (0.00). For a nonzero value whose magnitude is below 0.1 this also keeps the zeros after the point: -0.0836 is stored asc = [0,8,3,6], e = -1instead ofc = [8,3,6], e = -2.P.roundworks out where to round fromthis.e, but rounds a copy, and the copy constructor normalizes. So the precision calculation uses a different exponent from the value being rounded. #4962 fixed the formatting side of the same representation, and 1c02557 worked around it intoFixed.The same representation made
GetHashCodediffer between equal decimals, since it hashescandedirectly. As a result,HashSet<decimal>andDictionary<decimal, _>treateddecimal -0.0836and0M - 0.0836Mas different keys.Fix
After
parse()'s existing rounding step, normalize the value when it has a nonzero digit. Zero is left as is, so0.00still keeps its scale. The rounding toBig.DP + 1digits happens before the normalization, so precision is unchanged.Testing
tests/Js/Main/ArithmeticTests.fs: rounding below 0.1 (for decimals from doubles, strings and literals), and hash consistency (GetHashCode,HashSet,Dictionary). Both fail without the fix and pass with it. They also pass on .NET.🤖 Generated with Claude Code