From 3e38bd83aecf16bb7ae711f0120a1d6eafeaeb18 Mon Sep 17 00:00:00 2001 From: kzrnm Date: Fri, 28 Feb 2025 00:51:07 +0900 Subject: [PATCH] Fix the unsigned right shift operator of BigInteger (#112879) * Add tests for the shift operator of BigInteger * Fix the unsigned right shift operator of BigInteger * avoid stackalloc * external sign element --- .../src/System/Numerics/BigInteger.cs | 25 +- .../tests/BigInteger/MyBigInt.cs | 59 ++++ .../tests/BigInteger/op_leftshift.cs | 64 +++++ .../tests/BigInteger/op_rightshift.cs | 265 ++++++++++++------ .../tests/BigInteger/stackcalculator.cs | 2 + 5 files changed, 324 insertions(+), 91 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 32163b0fcf8701..36039a9638a2d3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -5271,13 +5271,32 @@ static bool INumberBase.TryConvertToTruncating(BigInteger va BigInteger result; + bool negx = value._sign < 0; + uint smallBits = NumericsHelpers.Abs(value._sign); + scoped ReadOnlySpan bits = value._bits; + if (bits.IsEmpty) + { + bits = new ReadOnlySpan(in smallBits); + } + + int xl = bits.Length; + if (negx && (bits[^1] >= kuMaskHighBit) && ((bits[^1] != kuMaskHighBit) || bits.IndexOfAnyExcept(0u) != (bits.Length - 1))) + { + // For a shift of N x 32 bit, + // We check for a special case where its sign bit could be outside the uint array after 2's complement conversion. + // For example given [0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF], its 2's complement is [0x01, 0x00, 0x00] + // After a 32 bit right shift, it becomes [0x00, 0x00] which is [0x00, 0x00] when converted back. + // The expected result is [0x00, 0x00, 0xFFFFFFFF] (2's complement) or [0x00, 0x00, 0x01] when converted back + // If the 2's component's last element is a 0, we will track the sign externally + ++xl; + } + uint[]? xdFromPool = null; - int xl = value._bits?.Length ?? 1; Span xd = (xl <= BigIntegerCalculator.StackAllocThreshold ? stackalloc uint[BigIntegerCalculator.StackAllocThreshold] : xdFromPool = ArrayPool.Shared.Rent(xl)).Slice(0, xl); - - bool negx = value.GetPartsForBitManipulation(xd); + xd[^1] = 0; + bits.CopyTo(xd); if (negx) { diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs index 96699257a4133d..2646d85e8df501 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs @@ -108,6 +108,8 @@ public static BigInteger DoBinaryOperatorMine(BigInteger num1, BigInteger num2, return new BigInteger(Max(bytes1, bytes2).ToArray()); case "b>>": return new BigInteger(ShiftLeft(bytes1, Negate(bytes2)).ToArray()); + case "b>>>": + return new BigInteger(ShiftRightUnsigned(bytes1, bytes2).ToArray()); case "b<<": return new BigInteger(ShiftLeft(bytes1, bytes2).ToArray()); case "bRotateLeft": @@ -641,11 +643,68 @@ public static List Not(List bytes) return bnew; } + public static List ShiftRightUnsigned(List bytes1, List bytes2) + { + int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List(new byte[] { 8 })).ToArray()); + sbyte bitShift = (sbyte)new BigInteger(Remainder(Copy(bytes2), new List(new byte[] { 8 })).ToArray()); + + if (byteShift == 0 && bitShift == 0) + return bytes1; + + if (byteShift < 0 || bitShift < 0) + return ShiftLeft(bytes1, Negate(bytes2)); + + Trim(bytes1); + + byte fill = (bytes1[bytes1.Count - 1] & 0x80) != 0 ? byte.MaxValue : (byte)0; + + if (fill == byte.MaxValue) + { + while (bytes1.Count % 4 != 0) + { + bytes1.Add(fill); + } + } + + if (byteShift >= bytes1.Count) + { + return [fill]; + } + + if (fill == byte.MaxValue) + { + bytes1.Add(0); + } + + for (int i = 0; i < bitShift; i++) + { + bytes1 = ShiftRight(bytes1); + } + + List temp = new List(); + for (int i = byteShift; i < bytes1.Count; i++) + { + temp.Add(bytes1[i]); + } + bytes1 = temp; + + if (fill == byte.MaxValue && bytes1.Count % 4 == 1) + { + bytes1.RemoveAt(bytes1.Count - 1); + } + + Trim(bytes1); + + return bytes1; + } + public static List ShiftLeft(List bytes1, List bytes2) { int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List(new byte[] { 8 })).ToArray()); sbyte bitShift = (sbyte)new BigInteger(Remainder(bytes2, new List(new byte[] { 8 })).ToArray()); + Trim(bytes1); + for (int i = 0; i < Math.Abs(bitShift); i++) { if (bitShift < 0) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs index 47b0a8ea7678cd..6c278bb63edb6e 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_leftshift.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Xunit; namespace System.Numerics.Tests @@ -151,6 +152,56 @@ public static void RunLeftShiftTests() } } + [Fact] + public void RunSmallTests() + { + foreach (int i in new int[] { + 0, + 1, + 16, + 31, + 32, + 33, + 63, + 64, + 65, + 100, + 127, + 128, + }) + { + foreach (int shift in new int[] { + 0, + -1, 1, + -16, 16, + -31, 31, + -32, 32, + -33, 33, + -63, 63, + -64, 64, + -65, 65, + -100, 100, + -127, 127, + -128, 128, + }) + { + var num = Int128.One << i; + for (int k = -1; k <= 1; k++) + { + foreach (int sign in new int[] { -1, +1 }) + { + Int128 value128 = sign * (num + k); + + byte[] tempByteArray1 = GetRandomSmallByteArray(value128); + byte[] tempByteArray2 = GetRandomSmallByteArray(shift); + + VerifyLeftShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b<<"); + } + } + } + } + } + private static void VerifyLeftShiftString(string opstring) { StackCalc sc = new StackCalc(opstring); @@ -160,6 +211,19 @@ private static void VerifyLeftShiftString(string opstring) } } + private static byte[] GetRandomSmallByteArray(Int128 num) + { + byte[] value = new byte[16]; + + for (int i = 0; i < value.Length; i++) + { + value[i] = (byte)num; + num >>= 8; + } + + return value; + } + private static byte[] GetRandomByteArray(Random random) { return GetRandomByteArray(random, random.Next(0, 1024)); diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs index 404902f61972e7..c725d52bc0ba4c 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/op_rightshift.cs @@ -1,92 +1,29 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Generic; using Xunit; namespace System.Numerics.Tests { - public class op_rightshiftTest + public abstract class op_rightshiftTestBase { + public abstract string opstring { get; } private static int s_samples = 10; private static Random s_random = new Random(100); [Fact] - public static void BigShiftsTest() - { - BigInteger a = new BigInteger(1); - BigInteger b = new BigInteger(Math.Pow(2, 31)); - - for (int i = 0; i < 100; i++) - { - BigInteger a1 = (a << (i + 31)); - BigInteger a2 = a1 >> i; - - Assert.Equal(b, a2); - } - } - - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // May fail on 32-bit due to a large memory requirement - public static void LargeNegativeBigIntegerShiftTest() + public void RunRightShiftTests() { - // Create a very large negative BigInteger - int bitsPerElement = 8 * sizeof(uint); - int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement); - BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1); - Assert.Equal(maxBitLength - 1, bigInt.GetBitLength()); - Assert.Equal(-1, bigInt.Sign); - - // Validate internal representation. - // At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros. - // Given this, bigInt._bits is expected to be structured as follows: - // - _bits.Length == ceil(maxBitLength / bitsPerElement) - // - First (_bits.Length - 1) elements: 0x00000000 - // - Last element: 0x80000000 - // ^------ (There's the leading '1') - - Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length); - - uint i = 0; - for (; i < (bigInt._bits.Length - 1); i++) { - Assert.Equal(0x00000000u, bigInt._bits[i]); - } - - Assert.Equal(0x80000000u, bigInt._bits[i]); - - // Right shift the BigInteger - BigInteger shiftedBigInt = bigInt >> 1; - Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength()); - Assert.Equal(-1, shiftedBigInt.Sign); - - // Validate internal representation. - // At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros. - // Given this, shiftedBigInt._bits is expected to be structured as follows: - // - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement) - // - First (_bits.Length - 1) elements: 0x00000000 - // - Last element: 0x40000000 - // ^------ (the '1' is now one position to the right) - - Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length); - - i = 0; - for (; i < (shiftedBigInt._bits.Length - 1); i++) { - Assert.Equal(0x00000000u, shiftedBigInt._bits[i]); - } - - Assert.Equal(0x40000000u, shiftedBigInt._bits[i]); - } - - [Fact] - public static void RunRightShiftTests() - { - byte[] tempByteArray1 = new byte[0]; - byte[] tempByteArray2 = new byte[0]; + byte[] tempByteArray1; + byte[] tempByteArray2; // RightShift Method - Large BigIntegers - large + Shift for (int i = 0; i < s_samples; i++) { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = GetRandomPosByteArray(s_random, 2); - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - small + Shift @@ -94,7 +31,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - 32 bit Shift @@ -102,7 +39,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = new byte[] { (byte)32 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - All One Uint Large BigIntegers - 32 bit Shift @@ -110,7 +47,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomLengthAllOnesUIntByteArray(s_random); tempByteArray2 = new byte[] { (byte)32 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Uint 0xffffffff 0x8000000 ... Large BigIntegers - 32 bit Shift @@ -118,7 +55,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(s_random); tempByteArray2 = new byte[] { (byte)32 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - large - Shift @@ -126,7 +63,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = GetRandomNegByteArray(s_random, 2); - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - small - Shift @@ -134,7 +71,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - -32 bit Shift @@ -142,7 +79,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = new byte[] { (byte)0xe0 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Large BigIntegers - 0 bit Shift @@ -150,7 +87,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random); tempByteArray2 = new byte[] { (byte)0 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - large + Shift @@ -158,7 +95,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = GetRandomPosByteArray(s_random, 2); - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - small + Shift @@ -166,7 +103,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = new byte[] { (byte)s_random.Next(1, 32) }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - 32 bit Shift @@ -174,14 +111,14 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = new byte[] { (byte)32 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - large - Shift for (int i = 0; i < s_samples; i++) { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = GetRandomNegByteArray(s_random, 2); - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - small - Shift @@ -189,7 +126,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = new byte[] { unchecked((byte)s_random.Next(-31, 0)) }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - -32 bit Shift @@ -197,7 +134,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = new byte[] { (byte)0xe0 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Small BigIntegers - 0 bit Shift @@ -205,7 +142,7 @@ public static void RunRightShiftTests() { tempByteArray1 = GetRandomByteArray(s_random, 2); tempByteArray2 = new byte[] { (byte)0 }; - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Positive BigIntegers - Shift to 0 @@ -217,7 +154,7 @@ public static void RunRightShiftTests() { Array.Reverse(tempByteArray2); } - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); } // RightShift Method - Negative BigIntegers - Shift to -1 @@ -229,7 +166,57 @@ public static void RunRightShiftTests() { Array.Reverse(tempByteArray2); } - VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + "b>>"); + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); + } + } + + [Fact] + public void RunSmallTests() + { + foreach (int i in new int[] { + 0, + 1, + 16, + 31, + 32, + 33, + 63, + 64, + 65, + 100, + 127, + 128, + }) + { + foreach (int shift in new int[] { + 0, + -1, 1, + -16, 16, + -31, 31, + -32, 32, + -33, 33, + -63, 63, + -64, 64, + -65, 65, + -100, 100, + -127, 127, + -128, 128, + }) + { + var num = Int128.One << i; + for (int k = -1; k <= 1; k++) + { + foreach (int sign in new int[] { -1, +1 }) + { + Int128 value128 = sign * (num + k); + + byte[] tempByteArray1 = GetRandomSmallByteArray(value128); + byte[] tempByteArray2 = GetRandomSmallByteArray(shift); + + VerifyRightShiftString(Print(tempByteArray2) + Print(tempByteArray1) + opstring); + } + } + } } } @@ -242,6 +229,19 @@ private static void VerifyRightShiftString(string opstring) } } + private static byte[] GetRandomSmallByteArray(Int128 num) + { + byte[] value = new byte[16]; + + for (int i = 0; i < value.Length; i++) + { + value[i] = (byte)num; + num >>= 8; + } + + return value; + } + private static byte[] GetRandomByteArray(Random random) { return GetRandomByteArray(random, random.Next(0, 1024)); @@ -292,7 +292,7 @@ private static byte[] GetRandomLengthFirstUIntMaxSecondUIntMSBMaxArray(Random ra int gap = random.Next(0, 128); int byteLength = 4 + gap * 4 + 1; byte[] array = new byte[byteLength]; - array[^6] = 0x80; + array[^5] = 0x80; array[^1] = 0xFF; return array; } @@ -302,4 +302,93 @@ private static string Print(byte[] bytes) return MyBigIntImp.Print(bytes); } } + public class op_rightshiftTest : op_rightshiftTestBase + { + public override string opstring => "b>>"; + + [Fact] + public static void BigShiftsTest() + { + BigInteger a = new BigInteger(1); + BigInteger b = new BigInteger(Math.Pow(2, 31)); + + for (int i = 0; i < 100; i++) + { + BigInteger a1 = (a << (i + 31)); + BigInteger a2 = a1 >> i; + + Assert.Equal(b, a2); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] // May fail on 32-bit due to a large memory requirement + public static void LargeNegativeBigIntegerShiftTest() + { + // Create a very large negative BigInteger + int bitsPerElement = 8 * sizeof(uint); + int maxBitLength = ((Array.MaxLength / bitsPerElement) * bitsPerElement); + BigInteger bigInt = new BigInteger(-1) << (maxBitLength - 1); + Assert.Equal(maxBitLength - 1, bigInt.GetBitLength()); + Assert.Equal(-1, bigInt.Sign); + + // Validate internal representation. + // At this point, bigInt should be a 1 followed by maxBitLength - 1 zeros. + // Given this, bigInt._bits is expected to be structured as follows: + // - _bits.Length == ceil(maxBitLength / bitsPerElement) + // - First (_bits.Length - 1) elements: 0x00000000 + // - Last element: 0x80000000 + // ^------ (There's the leading '1') + + Assert.Equal((maxBitLength + (bitsPerElement - 1)) / bitsPerElement, bigInt._bits.Length); + + uint i = 0; + for (; i < (bigInt._bits.Length - 1); i++) + { + Assert.Equal(0x00000000u, bigInt._bits[i]); + } + + Assert.Equal(0x80000000u, bigInt._bits[i]); + + // Right shift the BigInteger + BigInteger shiftedBigInt = bigInt >> 1; + Assert.Equal(maxBitLength - 2, shiftedBigInt.GetBitLength()); + Assert.Equal(-1, shiftedBigInt.Sign); + + // Validate internal representation. + // At this point, shiftedBigInt should be a 1 followed by maxBitLength - 2 zeros. + // Given this, shiftedBigInt._bits is expected to be structured as follows: + // - _bits.Length == ceil((maxBitLength - 1) / bitsPerElement) + // - First (_bits.Length - 1) elements: 0x00000000 + // - Last element: 0x40000000 + // ^------ (the '1' is now one position to the right) + + Assert.Equal(((maxBitLength - 1) + (bitsPerElement - 1)) / bitsPerElement, shiftedBigInt._bits.Length); + + i = 0; + for (; i < (shiftedBigInt._bits.Length - 1); i++) + { + Assert.Equal(0x00000000u, shiftedBigInt._bits[i]); + } + + Assert.Equal(0x40000000u, shiftedBigInt._bits[i]); + } + } + + public class op_UnsignedRightshiftTest : op_rightshiftTestBase + { + public override string opstring => "b>>>"; + + [Fact] + public void PowerOfTwo() + { + for (int i = 0; i < 32; i++) + { + foreach (int k in new int[] { 1, 2, 10 }) + { + Assert.Equal(BigInteger.One << i, (BigInteger.One << (32 * k + i)) >>> (32 * k)); + Assert.Equal(new BigInteger(unchecked((int)(uint.MaxValue << i))), (BigInteger.MinusOne << (32 * k + i)) >>> (32 * k)); + } + } + } + } } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs index 7160fd0f0582d4..cbd2a04127ef7f 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/stackcalculator.cs @@ -191,6 +191,8 @@ private BigInteger DoBinaryOperatorSN(BigInteger num1, BigInteger num2, string o return BigInteger.Max(num1, num2); case "b>>": return num1 >> (int)num2; + case "b>>>": + return num1 >>> (int)num2; case "b<<": return num1 << (int)num2; case "bRotateLeft":