Skip to content

Commit

Permalink
BigMul with 64-bit operands (dotnet#35975)
Browse files Browse the repository at this point in the history
* Unsigned 128-bit BigMul

* Signed 128-bit BigMul

* Added explanation of alg and reference to it

* Removed else branch

Co-authored-by: Adeel Mujahid <[email protected]>
  • Loading branch information
sakno and am11 authored May 23, 2020
1 parent a26363e commit 70ac9ae
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
47 changes: 47 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Math.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics.X86;
using System.Runtime.Versioning;

namespace System
Expand Down Expand Up @@ -113,6 +114,52 @@ public static long BigMul(int a, int b)
return ((long)a) * b;
}

[CLSCompliant(false)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ulong BigMul(ulong a, ulong b, out ulong low)
{
if (Bmi2.X64.IsSupported)
{
ulong tmp;
ulong high = Bmi2.X64.MultiplyNoFlags(a, b, &tmp);
low = tmp;
return high;
}

return SoftwareFallback(a, b, out low);

static ulong SoftwareFallback(ulong a, ulong b, out ulong low)
{
// It's adoption of algorithm for multiplication
// of 32-bit unsigned integers described
// in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8
// Basically, it's an optimized version of FOIL method applied to
// low and high dwords of each operand
const ulong lowBitsMask = 0xFFFFFFFFU;

ulong al = a & lowBitsMask;
ulong ah = a >> 32;
ulong bl = b & lowBitsMask;
ulong bh = b >> 32;

ulong mull = al * bl;
ulong t = ah * bl + (mull >> 32);
ulong tl = t & lowBitsMask;

tl += al * bh;
low = tl << 32 | mull & lowBitsMask;

return ah * bh + (t >> 32) + (tl >> 32);
}
}

public static long BigMul(long a, long b, out long low)
{
ulong high = BigMul((ulong)a, (ulong)b, out ulong ulow);
low = (long)ulow;
return (long)high - ((a >> 63) & b) - ((b >> 63) & a);
}

public static double BitDecrement(double x)
{
long bits = BitConverter.DoubleToInt64Bits(x);
Expand Down
30 changes: 30 additions & 0 deletions src/libraries/System.Runtime.Extensions/tests/System/Math.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1749,6 +1749,36 @@ public static void BigMul()
Assert.Equal(0L, Math.BigMul(0, 0));
}

[Theory]
[InlineData(0U, 0U, "00000000000000000000000000000000")]
[InlineData(0U, 1U, "00000000000000000000000000000000")]
[InlineData(1U, 0U, "00000000000000000000000000000000")]
[InlineData(2U, 3U, "00000000000000000000000000000006")]
[InlineData(ulong.MaxValue, 2, "0000000000000001FFFFFFFFFFFFFFFE")]
[InlineData(ulong.MaxValue, 1, "0000000000000000FFFFFFFFFFFFFFFF")]
[InlineData(ulong.MaxValue, ulong.MaxValue, "FFFFFFFFFFFFFFFE0000000000000001")]
[InlineData(ulong.MaxValue, 3, "0000000000000002FFFFFFFFFFFFFFFD")]
public static void BigMul128_Unsigned(ulong a, ulong b, string result)
{
ulong high = Math.BigMul(a, b, out ulong low);
Assert.Equal(result, high.ToString("X16") + low.ToString("X16"));
}

[Theory]
[InlineData(0L, 0L, "00000000000000000000000000000000")]
[InlineData(0L, 1L, "00000000000000000000000000000000")]
[InlineData(1L, 0L, "00000000000000000000000000000000")]
[InlineData(2L, 3L, "00000000000000000000000000000006")]
[InlineData(3L, -2L, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA")]
[InlineData(-1L, -1L, "00000000000000000000000000000001")]
[InlineData(-1L, long.MinValue, "00000000000000008000000000000000")]
[InlineData(1L, long.MinValue, "FFFFFFFFFFFFFFFF8000000000000000")]
public static void BigMul128_Signed(long a, long b, string result)
{
long high = Math.BigMul(a, b, out long low);
Assert.Equal(result, high.ToString("X16") + low.ToString("X16"));
}

[Theory]
[InlineData(1073741, 2147483647, 2000, 1647)]
[InlineData(6, 13952, 2000, 1952)]
Expand Down
3 changes: 3 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,9 @@ public static partial class Math
public static double Atan2(double y, double x) { throw null; }
public static double Atanh(double d) { throw null; }
public static long BigMul(int a, int b) { throw null; }
[System.CLSCompliantAttribute(false)]
public static ulong BigMul(ulong a, ulong b, out ulong low) { throw null; }
public static long BigMul(long a, long b, out long low) { throw null; }
public static double BitDecrement(double x) { throw null; }
public static double BitIncrement(double x) { throw null; }
public static double Cbrt(double d) { throw null; }
Expand Down

0 comments on commit 70ac9ae

Please sign in to comment.