Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,7 @@ private static BigInteger Add(ReadOnlySpan<uint> leftBits, int leftSign, ReadOnl
}

if (bitsFromPool != null)
ArrayPool<uint>.Shared.Return(bitsFromPool);
ArrayPool<uint>.Shared.Return(bitsFromPool);

return result;
}
Expand Down Expand Up @@ -2636,7 +2636,7 @@ public static implicit operator BigInteger(nuint value)

if (zdFromPool != null)
ArrayPool<uint>.Shared.Return(zdFromPool);
exit:
exit:
if (xdFromPool != null)
ArrayPool<uint>.Shared.Return(xdFromPool);

Expand Down Expand Up @@ -5239,13 +5239,32 @@ static bool INumberBase<BigInteger>.TryConvertToTruncating<TOther>(BigInteger va

BigInteger result;

bool negx = value._sign < 0;
uint smallBits = NumericsHelpers.Abs(value._sign);
scoped ReadOnlySpan<uint> bits = value._bits;
if (bits.IsEmpty)
{
bits = new ReadOnlySpan<uint>(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<uint> xd = (xl <= BigIntegerCalculator.StackAllocThreshold
? stackalloc uint[BigIntegerCalculator.StackAllocThreshold]
: xdFromPool = ArrayPool<uint>.Shared.Rent(xl)).Slice(0, xl);

bool negx = value.GetPartsForBitManipulation(xd);
xd[^1] = 0;
bits.CopyTo(xd);

if (negx)
{
Expand Down
59 changes: 59 additions & 0 deletions src/libraries/System.Runtime.Numerics/tests/BigInteger/MyBigInt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 "b^":
Expand Down Expand Up @@ -637,11 +639,68 @@ public static List<byte> Not(List<byte> bytes)
return bnew;
}

public static List<byte> ShiftRightUnsigned(List<byte> bytes1, List<byte> bytes2)
{
int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
sbyte bitShift = (sbyte)new BigInteger(Remainder(Copy(bytes2), new List<byte>(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<byte> temp = new List<byte>();
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<byte> ShiftLeft(List<byte> bytes1, List<byte> bytes2)
{
int byteShift = (int)new BigInteger(Divide(Copy(bytes2), new List<byte>(new byte[] { 8 })).ToArray());
sbyte bitShift = (sbyte)new BigInteger(Remainder(bytes2, new List<byte>(new byte[] { 8 })).ToArray());

Trim(bytes1);

for (int i = 0; i < Math.Abs(bitShift); i++)
{
if (bitShift < 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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));
Expand Down
Loading
Loading