-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix UInt128 to double conversion for values >= 2^104 #122534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a bug in the UInt128 to double conversion for values >= 2^104. The issue was caused by incorrect handling of precision loss during the conversion, which has been corrected by implementing proper sticky bit rounding.
- Fixed the rounding logic in the UInt128 to double explicit conversion operator
- Added a regression test to verify the fix
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/UInt128.cs | Changed the conversion logic to use a sticky bit (1 if any of the lower 24 bits are set, 0 otherwise) instead of OR-ing the lower 24 bits directly, ensuring proper rounding for large values |
| src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs | Added a regression test that validates the conversion of a large UInt128 value (>= 2^104) to double produces the correct result |
huoyaoyuan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please check if the issue is also present for Int128?
| // lowest 24 bits and then or's them back to ensure rounding stays correct. | ||
|
|
||
| double lower = BitConverter.UInt64BitsToDouble(TwoPow76Bits | ((ulong)(value >> 12) >> 12) | (value._lower & 0xFFFFFF)) - TwoPow76; | ||
| double lower = BitConverter.UInt64BitsToDouble(TwoPow76Bits | ((ulong)(value >> 12) >> 12) | ((value._lower & 0xFFFFFF) != 0 ? 1UL : 0UL)) - TwoPow76; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it correcting the behavior for some case? If not, it's a deoptimization.
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs
Outdated
Show resolved
Hide resolved
|
@dotnet-policy-service agree |
Yes it's also present for Int128, also this fix also applies to Int128 |
- Correctly calculate the sticky bit when converting large UInt128 values (>= 2^104) to double to prevent precision loss. - Optimize the upper bits extraction by accessing _upper directly instead of shifting. - Add regression tests for Int128 to ensure it is also fixed. - Update test comments to better explain the test cases. Fixes dotnet#122203
c808380 to
5473f4b
Compare
huoyaoyuan
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The numbers can be constructed more intuitively (also Int128 ones)
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt128Tests.cs
Outdated
Show resolved
Hide resolved
Use decimal literals instead of scientific notation and hex constructors instead of bit shifts for clearer test value representation.
Fixes #122203