-
Notifications
You must be signed in to change notification settings - Fork 5k
Simplify and fix the Int128 *, /, and % logic #75470
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1156,7 +1156,7 @@ public static Int128 Log2(Int128 value) | |
} | ||
|
||
// We simplify the logic here by just doing unsigned division on the | ||
// one's complement representation and then taking the correct sign. | ||
// two's complement representation and then taking the correct sign. | ||
|
||
ulong sign = (left._upper ^ right._upper) & (1UL << 63); | ||
|
||
|
@@ -1172,17 +1172,13 @@ public static Int128 Log2(Int128 value) | |
|
||
UInt128 result = (UInt128)(left) / (UInt128)(right); | ||
|
||
if (result == 0U) | ||
{ | ||
sign = 0; | ||
} | ||
else if (sign != 0) | ||
if (sign != 0) | ||
{ | ||
result = ~result + 1U; | ||
} | ||
|
||
return new Int128( | ||
result.Upper | sign, | ||
result.Upper, | ||
result.Lower | ||
); | ||
} | ||
|
@@ -1227,36 +1223,8 @@ public static Int128 Log2(Int128 value) | |
/// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" /> | ||
public static Int128 operator %(Int128 left, Int128 right) | ||
{ | ||
// We simplify the logic here by just doing unsigned modulus on the | ||
// one's complement representation and then taking the correct sign. | ||
|
||
ulong sign = (left._upper ^ right._upper) & (1UL << 63); | ||
|
||
if (IsNegative(left)) | ||
{ | ||
left = ~left + 1U; | ||
} | ||
|
||
if (IsNegative(right)) | ||
{ | ||
right = ~right + 1U; | ||
} | ||
|
||
UInt128 result = (UInt128)(left) % (UInt128)(right); | ||
|
||
if (result == 0U) | ||
{ | ||
sign = 0; | ||
} | ||
else if (sign != 0) | ||
{ | ||
result = ~result + 1U; | ||
} | ||
|
||
return new Int128( | ||
result.Upper | sign, | ||
result.Lower | ||
); | ||
Int128 quotient = left / right; | ||
return left - (quotient * right); | ||
tannergooding marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// | ||
|
@@ -1273,76 +1241,43 @@ public static Int128 Log2(Int128 value) | |
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" /> | ||
public static Int128 operator *(Int128 left, Int128 right) | ||
{ | ||
// We simplify the logic here by just doing unsigned multiplication on | ||
// the one's complement representation and then taking the correct sign. | ||
|
||
ulong sign = (left._upper ^ right._upper) & (1UL << 63); | ||
|
||
if (IsNegative(left)) | ||
{ | ||
left = ~left + 1U; | ||
} | ||
|
||
if (IsNegative(right)) | ||
{ | ||
right = ~right + 1U; | ||
} | ||
|
||
UInt128 result = (UInt128)(left) * (UInt128)(right); | ||
|
||
if (result == 0U) | ||
{ | ||
sign = 0; | ||
} | ||
else if (sign != 0) | ||
{ | ||
result = ~result + 1U; | ||
} | ||
|
||
return new Int128( | ||
result.Upper | sign, | ||
result.Lower | ||
); | ||
// Multiplication is the same for signed and unsigned provided the "upper" bits aren't needed | ||
return (Int128)((UInt128)(left) * (UInt128)(right)); | ||
tannergooding marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_CheckedMultiply(TSelf, TOther)" /> | ||
public static Int128 operator checked *(Int128 left, Int128 right) | ||
{ | ||
// We simplify the logic here by just doing unsigned multiplication on | ||
// the one's complement representation and then taking the correct sign. | ||
|
||
ulong sign = (left._upper ^ right._upper) & (1UL << 63); | ||
|
||
if (IsNegative(left)) | ||
{ | ||
left = ~left + 1U; | ||
} | ||
Int128 upper = BigMul(left, right, out Int128 lower); | ||
|
||
if (IsNegative(right)) | ||
if (((upper != 0) || (lower < 0)) && ((~upper != 0) || (lower >= 0))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something about this logic is hard to parse but I can't figure out a better way to structure it. |
||
{ | ||
right = ~right + 1U; | ||
} | ||
// The upper bits can safely be either Zero or AllBitsSet | ||
// where the former represents a positive value and the | ||
// latter a negative value. | ||
// | ||
// However, when the upper bits are Zero, we also need to | ||
// confirm the lower bits are positive, otherwise we have | ||
// a positive value greater than MaxValue and should throw | ||
tannergooding marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// | ||
// Likewise, when the upper bits are AllBitsSet, we also | ||
// need to confirm the lower bits are negative, otherwise | ||
// we have a large negative value less than MinValue and | ||
// should throw. | ||
|
||
UInt128 result = checked((UInt128)(left) * (UInt128)(right)); | ||
|
||
if ((long)(result.Upper) < 0) | ||
{ | ||
ThrowHelper.ThrowOverflowException(); | ||
} | ||
|
||
if (result == 0U) | ||
{ | ||
sign = 0; | ||
} | ||
else if (sign != 0) | ||
{ | ||
result = ~result + 1U; | ||
} | ||
return lower; | ||
} | ||
|
||
return new Int128( | ||
result.Upper | sign, | ||
result.Lower | ||
); | ||
internal static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) | ||
{ | ||
// This follows the same logic as is used in `long Math.BigMul(long, long, out long)` | ||
|
||
UInt128 upper = UInt128.BigMul((UInt128)(left), (UInt128)(right), out UInt128 ulower); | ||
lower = (Int128)(ulower); | ||
return (Int128)(upper) - ((left >> 127) & right) - ((right >> 127) & left); | ||
} | ||
|
||
// | ||
|
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
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
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.
Uh oh!
There was an error while loading. Please reload this page.