FLINT3.7.0-dev
Algorithm

Fixed-precision floating point with nfloat

Cost
O(n2)O(n^2) word operations per multiplication of nn-word numbers; a dot product of length ℓ\ell costs about ℓ\ell high products of n+1n + 1 words; matrix multiplication reduces to Strassen–Waksman fixed-point or multimodular integer matrix products.

The nfloat module implements floating-point numbers whose precision is a whole number nn of words, 1≤n≤661 \le n \le 66 (64 to 4224 bits on 64-bit machines). It is an example of a parametric type of the generic rings interface [Joh2025gr]: the element size is fixed when a context is created, so that elements need no heap memory and pack contiguously in vectors and matrices, while a single method table serves all precisions and each operation switches internally to a kernel for its number of words. For approximate polynomial root-finding and linear algebra, the generic algorithms run about twice as fast over nfloat as over arf_t or MPFR [Joh2025gr], because of lower overhead in the basic operations (no allocation, no precision or rounding-mode arguments, no checks for special values unless enabled) and faster vector and matrix kernels.

Representation and basic operations#

A normal element is (−1)s⋅m⋅2e−nw(-1)^s \cdot m \cdot 2^{e - nw} with ww the word size and the nn-word mantissa mm normalised, 2nw−1≤m<2nw2^{nw - 1} \le m < 2^{nw}. The sign occupies a whole word and special values (zero, infinities, NaN) are encoded in the exponent, so that the common case needs no bit manipulation and a single comparison detects all special values. The exponent is restricted to ±2w−2\pm 2^{w-2}, which leaves room to add two exponents and a correction without overflowing a word, so that overflow and underflow are detected by one comparison after each operation.

The basic operations dispatch on nn:

  • Addition and subtraction. With ∣x∣≥∣y∣|x| \ge |y| and δ=ex−ey\delta = e_x - e_y, the mantissa of yy is shifted right by δ\delta bits and truncated to nn words; if δ≥nw\delta \ge nw, yy is negligible. For n≤4n \le 4 there are separate kernels (_nfloat_add_1, ..., _nfloat_sub_4) that work in registers. After a subtraction, a cancellation is renormalised by counting leading zeros. The error is less than 2 ulp: at most 1 ulp from truncating yy and at most 1 ulp from truncating the result.
  • Multiplication. For one word, a 1×11 \times 1 word product; for two words, a full 2×22 \times 2 word product; from three words, the high product flint_mpn_mulhigh_n, which omits the low partial products at the cost of an error of a few units in an extra word, and is then normalised by at most one bit. Since the inputs are normalised, the product is at least 22nw−22^{2nw - 2}, so a one-bit shift always suffices.
  • Division and square root use 2/12/1 and 3/23/2 word divisions with precomputed inverses [GraMol2010] for one and two words, and approximate schoolbook division or Newton iterations with a guard word otherwise; the guard word is used to certify the direction of rounding when directed rounding is requested.

Directed rounding is implemented by computing the truncated result together with an inexactness flag (or an error bound in units of a guard word), and adding one ulp in the appropriate cases.

Dot products#

Sums of products are the inner loop of polynomial multiplication and of Gaussian elimination. Rounding each product and each partial sum to nn words, as a loop of nfloat_mul and nfloat_add would, costs normalisations and branches per term. _nfloat_vec_dot instead accumulates in fixed point, as arb_dot does [Joh2019dot].

Algorithm 1: Fixed-point dot product

Input: ss and xi,yix_i, y_i for 0≤i<ℓ0 \le i < \ell, with nn-word mantissas.

Output: an approximation of s+∑ixiyis + \sum_i x_i y_i.

  1. Find the largest exponent E0=max⁡i(exi+eyi)E_0 = \max_i (e_{x_i} + e_{y_i}) (and ese_s), skipping zero terms, and set E=E0+pE = E_0 + p with p=⌊log⁡2(ℓ+1)⌋+2p = \lfloor \log_2(\ell + 1) \rfloor + 2 padding bits.
  2. Accumulate in an (n+1)(n + 1)-word fixed-point number SS with unit 2E−(n+1)w2^{E - (n+1)w}: for each term, with d=E−(exi+eyi)d = E - (e_{x_i} + e_{y_i}), add or subtract the high product of the mantissas shifted right by dd bits, computing only the part above the unit; terms with d≥(n+1)wd \ge (n+1)w are skipped.
  3. Normalise SS and round it to nn words.

Proposition 1. In Algorithm 1, ∣S−(s+∑ixiyi)∣≤(cℓ+1) 2E−(n+1)w|S - (s + \sum_i x_i y_i)| \le (c\ell + 1)\, 2^{E - (n+1)w}, where cc bounds the error of one truncated shifted high product in units of the accumulator. The accumulator does not overflow, and the error is at most about cℓc\ell units in the guard word of the result, relative to the largest term.

Proof. Each term satisfies ∣xiyi∣<2exi+eyi≤2E−p|x_i y_i| < 2^{e_{x_i} + e_{y_i}} \le 2^{E - p}, so ∣s∣+∑i∣xiyi∣<(ℓ+1)2E−p≤2E−1|s| + \sum_i |x_i y_i| < (\ell + 1) 2^{E - p} \le 2^{E - 1} and the partial sums fit in the accumulator with a sign. A skipped term is less than one unit; a computed term is truncated at the unit with an error of at most cc units (the error of the high product plus the truncation of the shift), and ss is truncated once.

The implementation uses c=4c = 4 for n≤2n \le 2 (where the products are exact two- or three-word products held in registers) and c=n+2c = n + 2 otherwise, and with directed rounding adds or subtracts the bound cℓ+1c\ell + 1 before the final rounding, which makes the result a valid bound. A first pass over the exponents costs about 25% compared with maintaining the exponent on the fly, but keeps the inner loop simple. The complex dot product accumulates the real and imaginary parts in two such accumulators.

Matrix multiplication#

nfloat_mat_mul chooses, by dimension and precision, between the classical algorithm on top of the dot product and two reductions to exact or fixed-point arithmetic.

Fixed-point multiplication (nfloat_mat_mul_fixed). If the exponents of the entries of AA lie in a range of width ΔA\Delta_A, and those of BB in a range of width ΔB\Delta_B, then converting AA and BB to fixed point with a common exponent each, at precision p+ΔA+ΔBp + \Delta_A + \Delta_B plus a few guard bits, loses no relative precision in any entry. The product is then a fixed-point matrix product _nfixed_mat_mul, which needs no normalisation or exponent handling at all in its inner loops. It uses the classical algorithm with unrolled dot products of 2 to 8 words, Waksman's algorithm [Waksman1970], which saves about half of the multiplications in exchange for additions and pays off from about 9 words, or Strassen's algorithm [Str1969] above dimension 26 to 57. Since fixed-point numbers have no overflow handling, the inputs are scaled by 2−k2^{-k} with kk increased until _nfixed_mat_mul_bound certifies that every intermediate value of the chosen algorithm stays below 1; the same function bounds the error in ulps. The method fails (and the caller falls back) if the exponent spread is too large (more than 64 extra bits below 768 bits of precision, a quarter of the precision above) or if the matrices contain zeros.

Block multiplication (nfloat_mat_mul_block). For large matrices or entries with a wide range of exponents, the inner dimension is split into blocks within which each row of AA and each column of BB has entries of similar magnitude. Each block is scaled row by row and column by column to an integer matrix and multiplied exactly with fmpz_mat_mul, which uses multimodular or FFT methods; the only errors are from truncating the entries to integers and the final rounding [Joh2019dot]. This turns floating-point matrix multiplication into integer matrix multiplication with asymptotically fast algorithms.

Complex matrices are multiplied with three real products by the Karatsuba formula (AC−BD)+((A+B)(C+D)−AC−BD)i(AC - BD) + ((A + B)(C + D) - AC - BD)i in the fixed-point and block methods, or by separating real and imaginary parts into real matrices (nfloat_complex_mat_mul_reorder), which is used for matrices with real entries and for parts with very different scales. Linear algebra (nfloat_mat_lu, triangular solving) uses the recursive generic algorithms above tuned dimensions, so that most of its work runs on this matrix multiplication.

Use in FLINT#

The algorithms described above are implemented in src/nfloat: the arithmetic kernels in nfloat.c and div.c, the dot products in dot.c (_nfloat_vec_dot, _nfloat_complex_vec_dot), the fixed-point arithmetic in nfixed.c and the matrix multiplication in mat_mul.c. The elementary functions use the fixed-point kernels of the mp_real module. The nfloat contexts are the default choice for fast approximate arithmetic in the generic modules, for example for polynomial root-finding with gr_poly and approximate linear algebra with gr_mat.

int _nfloat_vec_dot(nfloat_ptr res, nfloat_srcptr initial, int subtract, nfloat_srcptr x, nfloat_srcptr y, slong len, gr_ctx_t ctx)
int nfloat_mat_mul(gr_mat_t C, const gr_mat_t A, const gr_mat_t B, gr_ctx_t ctx)
int nfloat_mat_mul_fixed(gr_mat_t C, const gr_mat_t A, const gr_mat_t B, slong max_extra_prec, gr_ctx_t ctx)
int nfloat_mat_mul_block(gr_mat_t C, const gr_mat_t A, const gr_mat_t B, slong min_block_size, gr_ctx_t ctx)

Referenced from#

References#

  • [Joh2025gr] F. Johansson. Generic rings in FLINT. Preprint, 2025. https://fredrikj.net/math/flintgenerics.pdf
  • [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
  • [Joh2019dot] F. Johansson. Faster arbitrary-precision dot product and matrix multiplication. Proceedings of the 26th IEEE Symposium on Computer Arithmetic (ARITH-26), 2019, 15–22. https://arxiv.org/abs/1901.04289
  • [Waksman1970] Waksman, Abraham, "On Winograd's algorithm for inner products", IEEE Transactions on Computers 19.04 (1970): 360-361.
  • [Str1969] V. Strassen, "Gaussian elimination is not optimal", Numerische Mathematik 13 (1969), 354-356, https://doi.org/10.1007/BF02165411
Cite this page

The FLINT team, “Fixed-precision floating point with nfloat,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/floating-point/nfloat.html (generated 2026-11-02).