Division with a precomputed inverse
#include <flint/ulong_extras.h>Returns the precomputed inverse of n \ne 0 used by all _preinv functions [GraMol2010]. n_preinvert_limb_prenorm is a faster version for normalised n (top bit set).
Returns a \bmod n. No restrictions on a; n \ne 0.
Returns a \bmod n and sets *q to \lfloor a/n \rfloor. The last two variants take the number of leading zero bits of n as norm; n_divrem_preinv_unnorm requires \mathit{norm} > 0.
Returns the quotient \lfloor a/n \rfloor. No restrictions on a; n \ne 0.
Returns a \bmod n for the two-limb integer a = a_{hi} B + a_{lo}. No restrictions on a; n \ne 0.
Division by a word n costs tens of cycles in hardware. When n is used more than a few times, it is faster to compute a reciprocal of n once with n_preinvert_limb and divide with multiplications. The functions below accept any nonzero n and any dividend, with two exceptions: n_lll_mod_preinv requires its top word to be reduced, and n_divrem_preinv_unnorm requires n not to be normalised.
Usage#
The value returned by n_preinvert_limb belongs to n and must be passed together with it. It is the reciprocal of the normalised divisor n 2^s, where s is the number of leading zero bits of n. Functions such as n_mulmod_preinv that require a normalised modulus take the same inverse, so one call serves both kinds of functions.
n_preinvert_limb_prenorm computes the same value without the shift when n is already normalised. n_divrem_preinv and n_divrem_preinv_unnorm take the shift s as an argument, saving its recomputation when many divisions share one n.
Algorithm#
The dividend is shifted left by s bits into two words, and the quotient is estimated as the high word of v u_1 + u, where v is the reciprocal and u_1 the high word of the shifted dividend. The estimate is off by at most one in each direction, which is fixed with one comparison and, rarely, a second one. The remainder is shifted back at the end.
n_ll_mod_preinv first reduces the high word modulo n (if it is not already smaller than n) and then performs one division step; n_lll_mod_preinv performs two steps.
Example#
#include <flint/flint.h>
#include <flint/ulong_extras.h>
int main(void)
{
ulong n = 1000003;
ulong ninv = n_preinvert_limb(n); /* one division, done once */
ulong a, q, r, hi, lo, wrong = 0;
r = n_divrem2_preinv(&q, 987654321, n, ninv);
flint_printf("987654321 = %wu * %wu + %wu\n", q, n, r);
for (a = 0; a < 1000000; a++)
wrong += (n_mod2_preinv(7919 * a, n, ninv) != (7919 * a) % n);
flint_printf("disagreements with %%: %wu\n", wrong);
/* reduce a full double-word product */
umul_ppmm(hi, lo, UWORD_MAX / 3, UWORD_MAX / 5);
r = n_ll_mod_preinv(hi, lo, n, ninv);
flint_printf("same as n_mulmod2_preinv: %s\n",
r == n_mulmod2_preinv(UWORD_MAX / 3, UWORD_MAX / 5, n, ninv) ? "yes" : "no");
return 0;
}987654321 = 987 * 1000003 + 651360 disagreements with %: 0 same as n_mulmod2_preinv: yes
Guides#
- Two-word arithmetic with longlong.h (
longlong) - Portable vec4d code (
machine_vectors)
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
The FLINT team, “Division with a precomputed inverse (ulong_extras.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/ulong_extras/preinv.html (generated 2026-10-20).
@misc{flint-doc-ulong-extras-preinv,
author = {{The FLINT team}},
title = {{Division with a precomputed inverse (ulong\_extras.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/ulong_extras/preinv.html},
note = {Generated 2026-10-20}
}