-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Provide support for alternative rounding modes for division and remainder of division #106795
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
|
Note regarding the |
|
Note regarding the |
src/libraries/System.Private.CoreLib/src/System/Numerics/IBinaryInteger.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Numerics/DivisionRounding.cs
Outdated
Show resolved
Hide resolved
|
This is also duplicated with #104589 |
I overlooked this PR. My PR has many tests that you can use. |
Corrected the file path for `DivisionRounding.cs` from a lowercase 's' in "system" to an uppercase 'S' in "System" to ensure consistency with the project structure.
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 implements support for alternative division rounding modes by introducing a new DivisionRounding enum and extending the IBinaryInteger<TSelf> interface with new overloads for Divide, DivRem, and Remainder methods that accept a DivisionRounding parameter. The implementation provides five rounding strategies: Truncate, Floor, Ceiling, AwayFromZero, and Euclidean.
Key Changes:
- Added
DivisionRoundingenum with five rounding modes - Extended
IBinaryInteger<TSelf>interface with rounding mode overloads for division operations - Added comprehensive test coverage for all integer types across all rounding modes
Reviewed Changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| DivisionRounding.cs | Defines the new enum with five division rounding modes |
| IBinaryInteger.cs | Implements default logic for division operations with rounding modes |
| System.Runtime.cs | Adds API surface for the new enum and interface methods |
| System.Private.CoreLib.Shared.projitems | Registers the new DivisionRounding.cs file in the build |
| GenericMathHelpers.cs | Adds test helper methods to compute expected values for different rounding modes |
| *Tests.GenericMath.cs (multiple) | Expands existing DivRem tests and adds new tests for mode-based division operations |
| // Graph for ceiling division with negative divisor https://www.wolframalpha.com/input?i=Plot%5B%7BCeiling%5B-n%5D%2C+n+%2B+Ceiling%5B-n%5D%7D%2C+%7Bn%2C+-3%2C+3%7D%5D | ||
|
|
||
| /// <summary> | ||
| /// AwayFromZero division (rounding away zero — round the division result away from zero to the nearest integer. |
Copilot
AI
Oct 24, 2025
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.
Missing closing parenthesis in the documentation comment. The opening parenthesis after 'zero' should be closed before the em dash.
| /// AwayFromZero division (rounding away zero — round the division result away from zero to the nearest integer. | |
| /// AwayFromZero division (rounding away zero) — round the division result away from zero to the nearest integer. |
| } | ||
| default: | ||
| ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); | ||
| break; |
Copilot
AI
Oct 24, 2025
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 ThrowArgumentException_InvalidEnumValue call is not wrapped in a throw statement. While ThrowHelper methods typically throw exceptions internally, this break statement is unreachable and should be removed for clarity, or the method call should explicitly throw.
| break; |
| break; | ||
| } | ||
| default: | ||
| ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); |
Copilot
AI
Oct 24, 2025
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 ThrowArgumentException_InvalidEnumValue call is not wrapped in a throw statement. While ThrowHelper methods typically throw exceptions internally, this break statement is unreachable and should be removed for clarity, or the method call should explicitly throw.
| } | ||
| default: | ||
| ThrowHelper.ThrowArgumentException_InvalidEnumValue(mode); | ||
| break; |
Copilot
AI
Oct 24, 2025
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 ThrowArgumentException_InvalidEnumValue call is not wrapped in a throw statement. While ThrowHelper methods typically throw exceptions internally, this break statement is unreachable and should be removed for clarity, or the method call should explicitly throw.
| break; |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test DivRemExpected instead of DivRem. The test should verify that DivRem throws the exception, not the helper method. Change to BinaryIntegerHelper<nint>.DivRem(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRemExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRem((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRem((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivRem((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test DivideExpected instead of Divide. The test should verify that Divide throws the exception, not the helper method. Change to BinaryIntegerHelper<nint>.Divide(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.DivideExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Divide((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Divide((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Divide((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test RemainderExpected instead of Remainder. The test should verify that Remainder throws the exception, not the helper method. Change to BinaryIntegerHelper<nint>.Remainder(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.RemainderExpected((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Remainder((nint)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Remainder((nint)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<nint>.Remainder((nint)0xFFFFFFFFFFFFFFFF, 0, mode)); |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test DivRemExpected instead of DivRem. The test should verify that DivRem throws the exception, not the helper method. Change to BinaryIntegerHelper<long>.DivRem(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRemExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRem((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRem((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivRem((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test DivideExpected instead of Divide. The test should verify that Divide throws the exception, not the helper method. Change to BinaryIntegerHelper<long>.Divide(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.DivideExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Divide((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Divide((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Divide((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0x0000000000000000, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0x0000000000000001, 0, mode)); | ||
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Copilot
AI
Oct 24, 2025
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.
These assertions test RemainderExpected instead of Remainder. The test should verify that Remainder throws the exception, not the helper method. Change to BinaryIntegerHelper<long>.Remainder(...).
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.RemainderExpected((long)0xFFFFFFFFFFFFFFFF, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Remainder((long)0x0000000000000000, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Remainder((long)0x0000000000000001, 0, mode)); | |
| Assert.Throws<DivideByZeroException>(() => BinaryIntegerHelper<long>.Remainder((long)0xFFFFFFFFFFFFFFFF, 0, mode)); |
Close #93568