a * b % m
equals a * b - (a * b / m) * m
Use floating point arithmetic to approximate a * b / m
. The approximation leaves a value small enough for normal 64 bit integer operations, for m
up to 63 bits.
This method is limited by the significand of a double
, which is usually 52 bits.
uint64_t mod_mul_52(uint64_t a, uint64_t b, uint64_t m) {
uint64_t c = (double)a * b / m - 1;
uint64_t d = a * b - c * m;
return d % m;
}
This method is limited by the significand of a long double
, which is usually 64 bits or larger. The integer arithmetic is limited to 63 bits.
uint64_t mod_mul_63(uint64_t a, uint64_t b, uint64_t m) {
uint64_t c = (long double)a * b / m - 1;
uint64_t d = a * b - c * m;
return d % m;
}
These methods require that a
and b
be less than m
. To handle arbitrary a
and b
, add these lines before c
is computed.
a = a % m;
b = b % m;
In both methods, the final %
operation could be made conditional.
return d >= m ? d % m : d;