Division with a precomputed inverse
#include <flint/ulong_extras.h>Returns the precomputed inverse of used by all _preinv functions [GraMol2010]. n_preinvert_limb_prenorm is a faster version for normalised (top bit set).
Returns and sets *q to . The last two variants take the number of leading zero bits of as norm; n_divrem_preinv_unnorm requires .
Returns the quotient . No restrictions on ; .
Returns for the two-limb integer . No restrictions on ; .
Division by a word costs tens of cycles in hardware. When is used more than a few times, it is faster to compute a reciprocal of once with n_preinvert_limb and divide with multiplications. The functions below accept any nonzero and any dividend, with two exceptions: n_lll_mod_preinv requires its top word to be reduced, and n_divrem_preinv_unnorm requires not to be normalised.
Usage#
The value returned by n_preinvert_limb belongs to and must be passed together with it. It is the reciprocal of the normalised divisor , where is the number of leading zero bits of . 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 is already normalised. n_divrem_preinv and n_divrem_preinv_unnorm take the shift as an argument, saving its recomputation when many divisions share one .
Algorithm#
The dividend is shifted left by bits into two words, and the quotient is estimated as the high word of , where is the reciprocal and 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 (if it is not already smaller than ) 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-11-02).
@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-11-02}
}