Skip to content

Commit 9bbfa9a

Browse files
authored
Optimize BigInteger.CompareTo() (dotnet#96835)
* Improve BigInteger.CompareTo() * Add test cases * Safe * Remove unused usings * Update assertion
1 parent 678ae31 commit 9bbfa9a

File tree

3 files changed

+51
-33
lines changed

3 files changed

+51
-33
lines changed

src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,16 +1183,12 @@ public int CompareTo(BigInteger other)
11831183
return _sign < other._sign ? -1 : _sign > other._sign ? +1 : 0;
11841184
return -other._sign;
11851185
}
1186-
int cuThis, cuOther;
1187-
if (other._bits == null || (cuThis = _bits.Length) > (cuOther = other._bits.Length))
1186+
1187+
if (other._bits == null)
11881188
return _sign;
1189-
if (cuThis < cuOther)
1190-
return -_sign;
11911189

1192-
int cuDiff = GetDiffLength(_bits, other._bits, cuThis);
1193-
if (cuDiff == 0)
1194-
return 0;
1195-
return _bits[cuDiff - 1] < other._bits[cuDiff - 1] ? -_sign : _sign;
1190+
int bitsResult = BigIntegerCalculator.Compare(_bits, other._bits);
1191+
return _sign < 0 ? -bitsResult : bitsResult;
11961192
}
11971193

11981194
public int CompareTo(object? obj)
@@ -3117,16 +3113,6 @@ private bool GetPartsForBitManipulation(Span<uint> xd)
31173113
return _sign < 0;
31183114
}
31193115

3120-
internal static int GetDiffLength(uint[] rgu1, uint[] rgu2, int cu)
3121-
{
3122-
for (int iv = cu; --iv >= 0;)
3123-
{
3124-
if (rgu1[iv] != rgu2[iv])
3125-
return iv + 1;
3126-
}
3127-
return 0;
3128-
}
3129-
31303116
[Conditional("DEBUG")]
31313117
private void AssertValid()
31323118
{

src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.Utils.cs

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
5+
46
namespace System.Numerics
57
{
68
internal static partial class BigIntegerCalculator
@@ -15,22 +17,18 @@ internal const
1517

1618
public static int Compare(ReadOnlySpan<uint> left, ReadOnlySpan<uint> right)
1719
{
18-
if (left.Length < right.Length)
19-
return -1;
20-
if (left.Length > right.Length)
21-
return 1;
20+
Debug.Assert(left.Length <= right.Length || left.Slice(right.Length).ContainsAnyExcept(0u));
21+
Debug.Assert(left.Length >= right.Length || right.Slice(left.Length).ContainsAnyExcept(0u));
2222

23-
for (int i = left.Length - 1; i >= 0; i--)
24-
{
25-
uint leftElement = left[i];
26-
uint rightElement = right[i];
27-
if (leftElement < rightElement)
28-
return -1;
29-
if (leftElement > rightElement)
30-
return 1;
31-
}
23+
if (left.Length != right.Length)
24+
return left.Length < right.Length ? -1 : 1;
25+
26+
int iv = left.Length;
27+
while (--iv >= 0 && left[iv] == right[iv]) ;
3228

33-
return 0;
29+
if (iv < 0)
30+
return 0;
31+
return left[iv] < right[iv] ? -1 : 1;
3432
}
3533

3634
private static int ActualLength(ReadOnlySpan<uint> value)

src/libraries/System.Runtime.Numerics/tests/BigInteger/Comparison.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,40 @@ private static void RunPositiveTests(Random random)
344344

345345
BigInteger b1 = new BigInteger(byteArray);
346346
VerifyComparison(BigInteger.One, false, b1 / b1, false, 0);
347+
348+
// BigIntegers constructed with a byte[]
349+
{
350+
int byteLength = 17;
351+
var byteArray1 = new byte[byteLength];
352+
var byteArray2 = new byte[byteLength];
353+
354+
byteArray1.AsSpan().Fill(1);
355+
byteArray2.AsSpan().Fill(1);
356+
357+
// Equals
358+
byteArray1[0] = 2;
359+
byteArray2[0] = 2;
360+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, 0);
361+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, 0);
362+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
363+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);
364+
365+
// Smaller
366+
byteArray1[0] = 2;
367+
byteArray2[0] = 3;
368+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, -1);
369+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, 1);
370+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
371+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);
372+
373+
// Larger
374+
byteArray1[0] = 3;
375+
byteArray2[0] = 2;
376+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), false, 1);
377+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), true, -1);
378+
VerifyComparison(new BigInteger(byteArray1), false, new BigInteger(byteArray2), true, 1);
379+
VerifyComparison(new BigInteger(byteArray1), true, new BigInteger(byteArray2), false, -1);
380+
}
347381
}
348382

349383
private static void RunNegativeTests(Random random)
@@ -636,7 +670,7 @@ private static void VerifyComparison(BigInteger x, bool IsXNegative, BigInteger
636670
Assert.Equal(expectedGreaterThan || expectedEquals, x >= y);
637671
Assert.Equal(expectedLessThan || expectedEquals, y >= x);
638672
}
639-
673+
640674
private static void VerifyCompareResult(int expected, int actual)
641675
{
642676
if (0 == expected)

0 commit comments

Comments
 (0)