Here is a reasonably fast implementation that uses the Math.BigMul
method (.NET 5 and later), and the decimal
data type:
/// <summary> Returns (a * b) % modulo </summary>
public static ulong ModuloMultiplication(ulong a, ulong b, ulong modulo)
{
ulong high = Math.BigMul(a, b, out var low);
if (high == 0) return low % modulo;
if (high <= 0xFFFFFFFF) return (ulong)(((decimal)a * b) % modulo);
decimal remainder = high;
remainder *= 0x100000000;
remainder += low >> 32;
remainder %= modulo;
remainder *= 0x100000000;
remainder += low & 0xFFFFFFFF;
remainder %= modulo;
Debug.Assert((BigInteger)remainder == ((BigInteger)a * b) % modulo); // Validation
return (ulong)remainder;
}
This implementation performs the operation in two steps, in order to overcome the 96 bit limitation of the decimal
type. It's almost 2 times faster than using the BigInteger
type, and it doesn't allocate memory. In case the product of the two numbers fits in the decimal range, the result is obtained in one step.
For a similar but slightly slower implementation that doesn't depend on the Math.BigMul
method (source code), you can look at the 4th revision of this answer.
Update: The (a * b) % c
operation could be even faster if you could use a native UInt128
type. Unfortunately this type does not exist. If you are feeling adventurous you could use the Dirichlet .NET Number Theory Library by Rick Sladkey (MIT License), that includes this type (along with the Int128
). You could just download the UInt128.cs code file, and include it in your project. Then you could do this:
public static ulong ModuloMultiplication(ulong a, ulong b, ulong modulo)
{
return (new Dirichlet.Numerics.UInt128(a) * b) % modulo;
}
It's around 2.5 times faster than the BigInteger
in my PC.
This code file has ~2.700 lines of code. if you are interested only in the (a * b) % c
functionality, you could probably strip it down to 150 lines or so, if you think it's worth the effort.