% by a run-time divisor costs 20–90 cycles; the compiler only turns division by a compile-time constant into multiplications. When a hot loop reduces modulo a value read from input (or a 64-bit modulus in Miller–Rabin / Pollard rho), replace the division by multiplications:
| Tool | Modulus | Idea |
|---|---|---|
| Barrett | any , run time | precompute ; the estimated quotient is off by at most 1 |
| Lemire fastmod | , operands | 64-bit fractional reciprocal, two multiplications |
| Montgomery | odd | keep ; reduce products with REDC (no division at all) |
mulmod64 | long double estimate of , exact correction |
using u64 = unsigned long long; using u128 = __uint128_t;
struct Barrett { // x % m for any x < 2^64
u64 m, im;
Barrett(u64 m) : m(m), im(~0ULL / m) {}
u64 reduce(u64 x) const { u64 q = (u64)(((u128)im * x) >> 64), r = x - q * m; return r >= m ? r - m : r; }
u64 mul(u64 a, u64 b) const { return reduce(a * b); } // a, b < m < 2^32
};
struct FastMod32 { // Lemire: x % d for x, d < 2^32
u64 M; unsigned d;
FastMod32(unsigned d) : M(~0ULL / d + 1), d(d) {}
unsigned reduce(unsigned x) const { u64 low = M * x; return (unsigned)(((u128)low * d) >> 64); }
};
struct Montgomery64 { // odd n < 2^62, values kept as a * 2^64 mod n
u64 n, ninv, r2;
Montgomery64(u64 n) : n(n), ninv(n) {
for (int i = 0; i < 6; i++) ninv *= 2 - n * ninv; // n^{-1} mod 2^64 (Newton: bits double)
ninv = -ninv; r2 = (u64)(-(u128)n % n); // -n^{-1}, and 2^128 mod n
}
u64 redc(u128 t) const { u64 m = (u64)t * ninv; u64 r = (u64)((t + (u128)m * n) >> 64); return r >= n ? r - n : r; }
u64 to(u64 a) const { return redc((u128)(a % n) * r2); }
u64 val(u64 a) const { return redc(a); }
u64 mul(u64 a, u64 b) const { return redc((u128)a * b); } // both in Montgomery form
u64 add(u64 a, u64 b) const { a += b; return a >= n ? a - n : a; }
u64 pow(u64 a, u64 e) const { u64 r = to(1); for (; e; e >>= 1, a = mul(a, a)) if (e & 1) r = mul(r, a); return r; }
};
u64 mulmod64(u64 a, u64 b, u64 m) { // a, b < m <= 7.2e18, no __int128
ll r = (ll)(a * b - m * (u64)(1.L / m * a * b));
return r + m * (r < 0) - m * (r >= (ll)m);
}Montgomery form is closed under addition and subtraction, equality is preserved (the map is a bijection), and only multiplication needs redc. Convert in once, compute many products (Miller–Rabin powers, Pollard rho iterations, NTT butterflies, RSA/ECC exponentiation — Montgomery is why modular exponentiation is fast in every crypto library), convert out once. In practice Barrett is roughly 4x faster than % by a run-time divisor, and Montgomery another 2-3x faster than Barrett when the modulus is fixed for many multiplications — the gap is this large because Barrett still does two full-width multiplications per %, while Montgomery’s redc replaces the division entirely and the conversion overhead amortizes away.
Montgomery needs an odd modulus
must be odd for to exist (the Newton iteration requires it). For an even modulus, use Barrett reduction instead, or split the even part out via CRT (CRT) and run Montgomery on the odd factor.
Proof: Barrett and Lemire
Barrett: let , so . Then , and for . So and : one conditional subtraction gives . (
mulneeds .)Lemire: (for not a power of two; exact otherwise), and is the fractional part of scaled by , up to an error units of . Multiplying the fractional part by and taking the integer part recovers exactly because the accumulated error never reaches the next integer of the scaled product.
Proof: Montgomery REDC and the Newton inverse
REDC: with and , set . Then , so is an integer, and gives . If then , so one subtraction suffices. With inputs , (both ), ;
tomultiplies by and reduces once;valreduces once to remove the factor . Newton: if , then satisfies ; starting from (, 3 correct bits) six iterations reach 64 bits. Range: keeps .
Proof: the
long doublemulmod (for )Let . Each of the three
long doubleoperations (64-bit mantissa) has relative error at most , so the computed value of is within about of the truth, and after truncation . Hence . The expressiona * b - m * qis evaluated modulo but its true value lies in , so the cast to signed gives it exactly, and one correction in either direction lands in . KACTL’s finer error analysis extends the range to (verified by random testing); near it fails, so use__int128there. It requires the x87 80-bitlong double.