Division by an invariant word
- Cost
- One 1 \times 1 \to 2 word product, one low product, a two-word addition and two comparisons. The reciprocal costs one division.
To divide a two-word number by a fixed single word without a hardware division, FLINT uses the algorithm of Möller and Granlund [GraMol2010] (Algorithm 4 in their paper), which multiplies by a precomputed reciprocal of the divisor. Every _preinv function in ulong_extras that reduces or divides by a single word uses it.
The reciprocal#
Let B = 2^{\mathtt{FLINT\_BITS}} and let d be normalised, meaning B/2 \le d < B. Its reciprocal is
Since B < (B^2 - 1)/d < 2B for normalised d, we have 0 < v < B and v fits in a word. Equivalently, v is the quotient of the two-word number (B - 1 - d) B + (B - 1) by d, which is how n_preinvert_limb_prenorm computes it with a single hardware division. On platforms without a fast two-by-one division instruction, a table lookup followed by Newton steps is used instead, as in GMP.
An arbitrary divisor d \ne 0 is handled by shifting: with s the number of leading zero bits of d, divide u 2^s by d 2^s and shift the remainder right by s bits. The quotient is unchanged. This is why n_preinvert_limb accepts any nonzero n: it returns the reciprocal of n 2^s.
The algorithm#
Input: normalised d, its reciprocal v, and u = u_1 B + u_0 with 0 \le u_1 < d and 0 \le u_0 < B.
Output: q, r with u = qd + r and 0 \le r < d.
- (q_1, q_0) \gets v u_1 + (u_1 B + u_0), a two-word result.
- q_1 \gets (q_1 + 1) \bmod B.
- r \gets (u_0 - q_1 d) \bmod B.
- If r > q_0: set q_1 \gets (q_1 - 1) \bmod B and r \gets (r + d) \bmod B.
- If r \ge d: set q_1 \gets q_1 + 1 and r \gets r - d.
- Return q = q_1 and r.
Step 4 can be implemented without branches using a mask, and step 5 is rarely taken. Compared with the older method of Granlund and Montgomery [GraMon1994], which needs a high product and a full product in the correction, this algorithm replaces one full product with a cheaper low product.
Correctness#
Let m = B + v = \lfloor (B^2 - 1)/d \rfloor and define k by md = B^2 - k. Then 1 \le k \le d, because B^2 - 1 - d < md \le B^2 - 1.
Lemma 1. Step 1 computes q_1 B + q_0 = m u_1 + u_0 exactly, with q_1 < B.
Proof. Indeed v u_1 + u_1 B + u_0 = m u_1 + u_0. Since md \le B^2 - 1 and u_1 \le d - 1, we get m u_1 + u_0 \le B^2 - 1 - m + B - 1 < B^2 because m > B.
Let q_1, q_0 denote the values after step 1, and let R = u - (q_1 + 1) d be the exact remainder belonging to the candidate quotient q_1 + 1 of step 2.
Lemma 2 (Bounds on the candidate remainder). With M = \max(q_0, B - d) we have q_0 - B < R < M and R \ge -d.
Proof. Multiply the identity of Lemma 1 by d and use md = B^2 - k: d q_1 B = (B^2 - k) u_1 + d u_0 - d q_0. Substituting into B R = B(u_1 B + u_0) - B d q_1 - B d gives
For the lower bounds, the first two terms are nonnegative, so R \ge d (q_0 - B)/B. As q_0 - B < 0 and d < B, this is greater than q_0 - B; as q_0 \ge 0, it is at least -d.
For the upper bound, use k \le d, u_1 \le d - 1 and u_0 \le B - 1:
Theorem 3. Algorithm 1 returns the correct quotient and remainder.
Proof. Step 3 computes r = R \bmod B, since u - (q_1 + 1) d \equiv u_0 - (q_1 + 1) d modulo B. Since d is normalised, B \le 2d. There are three cases.
- R < 0. Then r = R + B > q_0 by Lemma 2, so step 4 decrements the quotient and sets r = R + d, which lies in [0, d) because -d \le R < 0. Step 5 does nothing.
- R \ge 0 and r = R \le q_0. Step 4 does nothing, and 0 \le R < M < B \le 2d, so step 5 performs any necessary correction.
- R \ge 0 and r = R > q_0. This can only happen when M = B - d, so R < B - d. Step 4 wrongly decrements the quotient and sets r = R + d < B, but then d \le r < 2d, and step 5 undoes the change.
In all cases the final remainder is in [0, d). Every update of q_1 is an addition of \pm 1 modulo B, and the true quotient lies in [0, B) because u_1 < d, so computing the quotient modulo B gives the right answer even if step 2 wraps around.
Use in FLINT#
The reciprocal of The reciprocal is the ninv field of every nmod_t, so this algorithm is the default modular reduction for all of FLINT's arithmetic modulo word-size integers.
nmod_mulmultiplies and reduces the two-word product with one step; the macrosNMOD_RED,NMOD_RED2andNMOD_RED3reduce one, two and three words with one, one and two steps. They are used throughoutnmod_vec,nmod_poly,nmod_mat,nmod_mpolyand thegrcontexts for \mathbb{Z}/n\mathbb{Z}; in particular every dot product innmod_vecends with one such reduction (Dot products modulo a word-size integer).n_mod2_preinv,n_divrem2_preinvandn_div2_preinvshift a one-word dividend into two words and run Algorithm 1 once. The condition u_1 < d holds automatically after shifting.n_ll_mod_preinvfirst reduces the high word modulo n if necessary and then runs the algorithm on the two-word dividend.n_lll_mod_preinvreduces the top two words and then the bottom two.n_mulmod2_preinvmultiplies and reduces the two-word product withn_ll_mod_preinv;n_mulmod_preinvskips the normalisation shift by requiring a normalised modulus.
Referenced from#
- Barrett and Lemire reduction (algorithm)
- Montgomery multiplication (REDC) (algorithm)
- Modular multiplication with a multiword modulus (algorithm)
- Integer division and Newton iteration (algorithm)
- Dot products modulo a word-size integer (algorithm)
- Strong probable prime test (algorithm)
- Division and rounding conventions
udiv_qrnnd_preinv- mpn_extras.h (module)
- Basic arithmetic modulo n
nmod_t- nmod.h (module)
- Multiplication with a precomputed inverse
- Exponentiation with a precomputed inverse
- Division with a precomputed inverse
References#
- [GraMol2010] N. Möller and T. Granlund, "Improved division by invariant integers", IEEE Transactions on Computers 60 (2011), 165–175, https://gmplib.org/~tege/division-paper.pdf
- [GraMon1994] Törbjorn Granlund and Peter L. Montgomery : Division by Invariant Integers using Multiplication https://gmplib.org/~tege/divcnst-pldi94.pdf
The FLINT team, “Division by an invariant word,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/arithmetic/moller-granlund.html (generated 2026-10-20).
@misc{flint-doc-algorithms-arithmetic-moller-granlund,
author = {{The FLINT team}},
title = {{Division by an invariant word}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/arithmetic/moller-granlund.html},
note = {Generated 2026-10-20}
}