Elementary functions in double-word to quad-double precision
- Cost
- A constant number of -term expansion operations per function value: one or two table lookups, a polynomial of degree 3 to 15 with tapered precision, and a few full-precision products.
The dfloat module evaluates , , , , , and on expansions of doubles (see Double-word and multi-double arithmetic) as pure floating-point kernels with static error bounds: each kernel is a fixed sequence of expansion operations whose total error has been bounded once and for all, over the whole argument range, by a model of every operation in dev/dfloat_exp_bound.py. The ball versions then add the static bound to the radius instead of tracking the rounding errors operation by operation, and propagate the input radius with a bound on the derivative. The other functions (tan, sinh, cosh, tanh, asin, acos, atan2, pow, and the complex functions) are compositions of these kernels with the arithmetic.
The design follows the classical table-driven methods for double precision [Tan1989; Mul2016], adapted to expansions in three ways:
- Exact argument reduction. The reductions are arranged so that the cancelling operations on the head are exact (by Sterbenz's lemma and ulp counting), and the reduced argument keeps relative accuracy.
- Tapered precision. In Horner's rule for a polynomial in a small argument , the inner coefficients are multiplied by powers of , so their errors are scaled down; the inner steps are therefore done with fewer components (for example the schedule for the exponential at ). The schedules are chosen by the bound script.
- Several chains. A polynomial is split into its even and odd parts, and these again in even and odd parts, so that four independent chains in (or ) run in the four lanes of a SIMD register; the final combination is a single five-term sum. This shortens the dependent chain of expansion operations, which determines the latency of a scalar call. The vector functions (
_dN_vec_exp, ...) instead use the lanes for four different arguments.
Since tapered chains make the degree of a polynomial almost irrelevant to the latency, what matters is the number of full-precision operations, which a table lookup keeps at one or two. For the kernels are ordinary double code (one table level, polynomials by FMA) and about as fast as the C library.
Exponential#
Let be canonicalised. With , the 41-bit truncation of and the following terms of its expansion, the head is reduced to , then to with , and .
Lemma 1. For , the three subtractions computing , and are exact.
Proof. is exact since has 41 bits and . For , and are within a factor 2 of each other ( and ), so the difference is exact by Sterbenz's lemma; for both are multiples of and their difference is below , hence fits in 53 bits; for there is nothing to subtract. Similarly, and are differences of multiples of and of magnitude at most and , which is at most ulps when and , while and otherwise.
The reduced argument is assembled with the expansion kernels in an order that keeps every term significant. With (the product of two table entries, 179 and 257 entries of 5-term expansions) and ,
where and are the even and odd parts of the Taylor polynomial of , of degree 3, 6, 8 and 11 for . The scalar kernel splits and once more in , so that the four chains run in parallel and is one lane-wise product and one five-term sum. The result has relative error at most DFLOAT_EXP_EPS_N (about , , and ) for .
For expm1 with (where ), the constant term is replaced by from tables of and , so that the result is relative when is small or zero ( and exactly for ); beyond 0.34 it is , which loses nothing since there.
Sine and cosine#
With and the 33-bit truncation of , the head is reduced to ; this is exact for by the argument of Lemma 1, since then has at most 53 bits. The reduced argument is assembled and canonicalised (so that its tail is bounded by rather than , which matters for large ) and split as with . With , and from the products of two table levels (51 and 65 entries, using the sign symmetry) and ,
where and are Taylor polynomials. The results for are and according to . As for the exponential, the four chains in run in the SIMD lanes, as do the table products and the multipliers.
Since the argument is reduced with to about bits and the reduced argument has the absolute accuracy of the input, the error bounds DFLOAT_TRIG_EPS_N are absolute and scaled by : for the sine and for the cosine. Relative accuracy near the zeros at multiples of (other than 0) is not attempted. Arguments with , where would no longer be exact, are evaluated through arb_t.
Logarithm#
Write with , , the exponent and table index read from the bits of the head. The reduction uses two tabulated reciprocals: from 128 entries indexed by the leading bits of , giving (with on the two intervals adjacent to 1, which keeps relative near ), and with , giving with . The product is exact as a double-word , and with is computed with the head cancelled exactly, so that its error is rather than ; no division is needed. The tables hold for the rounded entries, so that
exactly. With , with and (degree 8, 8, 12 and 15 in ), evaluated as four chains in . The relative error bound DFLOAT_LOG_EPS_N holds for .
For log1p with , the argument is carried exactly into the reduction: the head with its rounding error and the tail of form an -term argument, whose reduction is computed to as above. For , is rounded to terms, and absorbs the rounding.
A reduction by division, with , would halve the number of coefficients by using only odd powers; but the even and odd parts of the polynomial already run in parallel lanes, so nothing would be gained for the price of a division, which costs much more than a reduction level by multiplication.
Arctangent#
For (odd symmetry) and : if , with , , ; if , with , , . In both cases , the head of the numerator is exact (Sterbenz), and the division is the long division kernel; for no division is done. With , , with 3, 7, 11 and 14 coefficients, evaluated as four chains in . One division is unavoidable here; a second reduction level would save only two tapered steps at the cost of a second division. The relative error bound DFLOAT_ATAN_EPS_N holds for all finite .
Balls#
For a ball and the kernel value at the midpoint with static relative bound :
- : radius , since for and ; is bounded by for and by a power of two otherwise.
- : radius , the derivative being largest at the left end of the ball (a lower bound for is computed from the head, the tail and the radius).
- : radius (the derivative is at most 1).
- , : radius with for the sine and for the cosine; a radius of 1 or more is replaced by the enclosure .
All radii are rounded up with the slack factor of Radius arithmetic and bumped by when tiny. Arguments outside the kernel ranges, infinite radii and nonfinite midpoints are handled through arb_t, so the ball functions are rigorous for every input.
Use in FLINT#
The kernels are dN_exp, dN_expm1, dN_sin, dN_cos, dN_sin_cos, dN_log, dN_log1p and dN_atan for , their ball versions dNb_exp, etc., and the vector versions _dN_vec_exp, _dN_vec_sin_cos, _dN_vec_log, _dN_vec_atan, which run the same kernels on four arguments at a time (the static bounds apply to every element). The tables are in src/dfloat/exp_tables.c, generated by dev/gen_dfloat_exp_tables.py. The vector functions are used by gr_powsum_sieved for the prime terms of the Riemann–Siegel main sum (Sieved power sums with dfloat balls).
Referenced from#
- Double-word and multi-double arithmetic (algorithm)
- Floating-point arithmetic in FLINT (overview)
- dfloat.h (module)
References#
- [Tan1989] P. T. P. Tang. Table-driven implementation of the exponential function in IEEE floating-point arithmetic. ACM Transactions on Mathematical Software 15:2 (1989), 144–157. https://doi.org/10.1145/63522.214389
- [Mul2016] J.-M. Muller. Elementary Functions: Algorithms and Implementation, third edition. Birkhäuser, 2016. https://doi.org/10.1007/978-1-4899-7983-4
The FLINT team, “Elementary functions in double-word to quad-double precision,” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/algorithms/floating-point/dfloat-elementary.html (generated 2026-11-02).
@misc{flint-doc-algorithms-floating-point-dfloat-elementary,
author = {{The FLINT team}},
title = {{Elementary functions in double-word to quad-double precision}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/algorithms/floating-point/dfloat-elementary.html},
note = {Generated 2026-11-02}
}