FLINT3.7.0-dev

Division with a precomputed inverse

Synopsis#include <flint/ulong_extras.h>
ulong n_preinvert_limb(ulong n)source
ulong n_preinvert_limb_prenorm(ulong n)source

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).

ulong n_mod2_preinv(ulong a, ulong n, ulong ninv)source

Returns a \bmod n. No restrictions on a; n \ne 0.

ulong n_divrem2_preinv(ulong * q, ulong a, ulong n, ulong ninv)source
ulong n_divrem_preinv(ulong * q, ulong a, ulong n, ulong ninv, unsigned int norm)source
ulong n_divrem_preinv_unnorm(ulong * q, ulong a, ulong n, ulong ninv, unsigned int norm)source

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.

ulong n_div2_preinv(ulong a, ulong n, ulong ninv)source

Returns the quotient \lfloor a/n \rfloor. No restrictions on a; n \ne 0.

ulong n_ll_mod_preinv(ulong a_hi, ulong a_lo, ulong n, ulong ninv)source

Returns a \bmod n for the two-limb integer a = a_{hi} B + a_{lo}. No restrictions on a; n \ne 0.

ulong n_lll_mod_preinv(ulong a_hi, ulong a_mi, ulong a_lo, ulong n, ulong ninv)source

Returns a \bmod n for the three-limb integer a = a_{hi} B^2 + a_{mi} B + a_{lo}. Requires a_{hi} < n.

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.

AlgorithmDivision by an invariant wordDivides a two-word number by a fixed word using one multiplication by a precomputed reciprocal, one low multiplication and two cheap corrections, instead of a hardware division.

Example#

Examplepreinv_basic.cchecked by make checkdoc
#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;
}
Output
987654321 = 987 * 1000003 + 651360
disagreements with %: 0
same as n_mulmod2_preinv: yes

Guides#

References#

Cite this page

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).