Correctly rounded arithmetic
#include <flint/decimal.h>int decfloat_neg(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_abs(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_add(decfloat_t res, const decfloat_t x, const decfloat_t y, gr_ctx_t ctx)sourceint decfloat_sub(decfloat_t res, const decfloat_t x, const decfloat_t y, gr_ctx_t ctx)sourceint decfloat_mul(decfloat_t res, const decfloat_t x, const decfloat_t y, gr_ctx_t ctx)sourceint decfloat_sqr(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_div(decfloat_t res, const decfloat_t x, const decfloat_t y, gr_ctx_t ctx)sourceint decfloat_inv(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_sqrt(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_rsqrt(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_add_ui(decfloat_t res, const decfloat_t x, ulong y, gr_ctx_t ctx)sourceint decfloat_add_si(decfloat_t res, const decfloat_t x, slong y, gr_ctx_t ctx)sourceint decfloat_add_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t y, gr_ctx_t ctx)sourceint decfloat_sub_ui(decfloat_t res, const decfloat_t x, ulong y, gr_ctx_t ctx)sourceint decfloat_sub_si(decfloat_t res, const decfloat_t x, slong y, gr_ctx_t ctx)sourceint decfloat_sub_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t y, gr_ctx_t ctx)sourceint decfloat_mul_ui(decfloat_t res, const decfloat_t x, ulong y, gr_ctx_t ctx)sourceint decfloat_mul_si(decfloat_t res, const decfloat_t x, slong y, gr_ctx_t ctx)sourceint decfloat_mul_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t y, gr_ctx_t ctx)sourceint decfloat_div_ui(decfloat_t res, const decfloat_t x, ulong y, gr_ctx_t ctx)sourceint decfloat_div_si(decfloat_t res, const decfloat_t x, slong y, gr_ctx_t ctx)sourceint decfloat_div_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t y, gr_ctx_t ctx)sourceint decfloat_mul_two(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_mul_10exp_si(decfloat_t res, const decfloat_t x, slong e, gr_ctx_t ctx)sourceint decfloat_mul_10exp_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t e, gr_ctx_t ctx)sourceint decfloat_mul_2exp_si(decfloat_t res, const decfloat_t x, slong e, gr_ctx_t ctx)sourceint decfloat_mul_2exp_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t e, gr_ctx_t ctx)sourceint decfloat_floor(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_ceil(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_trunc(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_nint(decfloat_t res, const decfloat_t x, gr_ctx_t ctx)sourceint decfloat_pow_ui(decfloat_t res, const decfloat_t x, ulong y, gr_ctx_t ctx)sourceint decfloat_pow_si(decfloat_t res, const decfloat_t x, slong y, gr_ctx_t ctx)sourceint decfloat_pow_fmpz(decfloat_t res, const decfloat_t x, const fmpz_t y, gr_ctx_t ctx)sourceArithmetic operations, correctly rounded to the context precision in the context rounding mode. Integer powers are computed exactly and then rounded once when the exact result has at most 100000 digits (always for powers of ten), and correctly rounded via arb otherwise. Multiplication by 10^e is exact for any e (a digit shift is only needed when e is not a multiple of the limb exponent). The integer rounding functions first round to an integer exactly and then round the integer to the context precision. Division by zero returns GR_DOMAIN unless infinities are admitted; square roots of negative numbers return GR_DOMAIN.
Multiplication by 2^e is exact followed by one rounding for |e| < 2^{16}, and otherwise correctly rounded through the binary conversion (so that huge |e| costs O(\log |e|) arithmetic).
In the exact ring, division and square root succeed exactly when the result is a decimal fraction (denominator of the form 2^i 5^j). Exact results with more than DECIMAL_CONV_DIGITS_LIMIT digits are not computed: products, integer powers (estimated before computing) and multiplications by 2^e then return GR_UNABLE.
int decfloat_neg_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_abs_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_add_round(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_sub_round(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_mul_round(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_sqr_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_div_round(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_inv_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_sqrt_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_rsqrt_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_mul_10exp_si_round(decfloat_t res, const decfloat_t x, slong e, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_mul_10exp_fmpz_round(decfloat_t res, const decfloat_t x, const fmpz_t e, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_mul_2exp_fmpz_round(decfloat_t res, const decfloat_t x, const fmpz_t e, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_floor_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_ceil_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_trunc_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceint decfloat_nint_round(decfloat_t res, const decfloat_t x, slong prec, int rnd, gr_ctx_t ctx)sourceVersions with an explicit precision and rounding mode, allowing variable-precision computations without modifying the context.
int _decfloat_add(decfloat_t res, const decfloat_t x, const decfloat_t y, int negate_y, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceint _decfloat_mul(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceint _decfloat_div(decfloat_t res, const decfloat_t x, const decfloat_t y, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceint _decfloat_sqrt(decfloat_t res, const decfloat_t x, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceint _decfloat_mul_10exp(decfloat_t res, const decfloat_t x, const fmpz_t e, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceint _decfloat_round_to_int(decfloat_t res, const decfloat_t x, int int_rnd, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceUnderlying implementations which additionally report rounding information (see decfloat_set_round_info). These are used by the ball arithmetic.
Addition aligns the operands by a limb offset and adds them exactly, unless the smaller operand lies entirely below the rounding horizon of the larger one, in which case the sum is computed as the larger operand plus a sticky tail (this makes additions with astronomically different exponents cost O(\mathrm{prec})). Multiplication first computes a truncated high product with a known error bound and rounds both ends of the resulting interval; if the two roundings agree the result is correctly rounded, and otherwise (with probability roughly n / B) the full product is computed. Division computes a truncated quotient with at least \mathrm{prec} + 1 digits by Euclidean division and uses the remainder as a sticky flag; square roots are computed likewise with radix_sqrtrem.
int _decfloat_set_round_limbs(decfloat_t res, nn_srcptr d, slong n, int negative, const fmpz_t exp, int eps, slong prec, int rnd, decimal_rounding_info * info, decmag_ptr err, gr_ctx_t ctx)sourceslong _decimal_round_mantissa(nn_ptr d, slong n, int negative, int eps, slong prec, int rnd, slong * newn, decimal_rounding_info * info, decmag_ptr err, const fmpz_t exp, gr_ctx_t ctx)sourceThe rounding core: sets res to (-1)^{\mathrm{negative}} (d, n) B^{\mathrm{exp}} (plus an unrepresented positive tail smaller than one unit of the lowest limb if eps is set) rounded to prec digits. Limbs of d may be zero at either end. The buffer must have room for n + 1 limbs (a carry may propagate out of the top) and is modified. The second function performs the in-place rounding, returning the number of low limbs to discard.
int decfloat_vec_dot(decfloat_t res, const decfloat_t initial, int subtract, decfloat_srcptr vec1, decfloat_srcptr vec2, slong len, gr_ctx_t ctx)sourceint decfloat_vec_dot_rev(decfloat_t res, const decfloat_t initial, int subtract, decfloat_srcptr vec1, decfloat_srcptr vec2, slong len, gr_ctx_t ctx)sourceThe gr dot product methods, implemented by the generic gr_generic_vec_dot and gr_generic_vec_dot_rev: each product and each sum is rounded separately, so the result is not correctly rounded.
decimal_rounding_infoStructure with fields inexact, increased (whether the magnitude was rounded up), underflow and overflow.
The arithmetic operations on decfloat_t return the exact result of the operation on the exact operands, rounded once to the precision in the rounding mode; the result is exact whenever it is representable. The plain functions use the context's precision and rounding mode, the _round functions take them as arguments, and the underscore functions additionally report what the rounding did.
Rounding#
A result is rounded to prec significant digits, counted from its leading nonzero digit. Rounding never depends on how the operands were stored: the trailing zero digits of a mantissa's lowest limb, which are an artefact of the limb alignment, do not count as significant. The seven rounding modes are toward zero, away from zero, toward -\infty, toward +\infty, and to nearest with ties to even, away from zero or toward zero. decfloat_set_round_info and the underscore functions report in a decimal_rounding_info whether the result is inexact, whether its magnitude was rounded up, and whether the exponent limits caused an underflow or overflow, and optionally write an upper bound for the rounding error to a decmag_t; the ball arithmetic adds this bound to the radius, which is why ball radii carry the exact rounding error rather than a whole ulp.
Algorithms#
The mantissas are integers in the radix B = 10^{19} (on 64-bit machines) and the exponents count limbs, so an addition aligns its operands by whole limbs and a rounding drops whole limbs and clears digits in one boundary limb. An addition whose smaller operand lies entirely below the rounding position of the larger is computed as the larger plus a sticky tail, in O(\mathit{prec}) time whatever the difference of exponents. A product is first computed as a truncated high product with an error bound; if both ends of the resulting interval round to the same value, that is the correctly rounded result, and otherwise (rarely) the full product is formed. Division and square root compute a truncated quotient or root with at least prec + 1 digits and use the remainder as a sticky bit. See Decimal floating-point arithmetic.
Composite operations#
Operations reached through the generic fallbacks of the gr interface are not correctly rounded, since they compose several rounded operations. This includes decfloat_vec_dot and hence matrix and polynomial arithmetic, gr_pow_fmpq, gr_hypot, gr_fac, gr_bin, power series functions, and gr_set_str applied to an expression such as "1/3 + 2/7". The complex arithmetic of deccfloat_t, on the other hand, rounds each component of a product or quotient once (_decfloat_dot2).
Example#
The example rounds a tie in three modes, adds numbers whose exponents differ by 10^{12}, and asks for the rounding error of 1/7 at three digits.
#include <flint/flint.h>
#include <flint/gr.h>
#include <flint/decimal.h>
int main(void)
{
gr_ctx_t ctx;
decfloat_t x, y, z;
decmag_t err;
decimal_rounding_info info;
int status = GR_SUCCESS;
gr_ctx_init_decfloat(ctx, 10, 0);
decfloat_init(x, ctx);
decfloat_init(y, ctx);
decfloat_init(z, ctx);
_decmag_init(err, ctx);
/* ties: 0.125 to two digits */
status |= decfloat_set_str(x, "0.125", ctx);
status |= decfloat_set_round(y, x, 2, DECIMAL_RND_NEAR, ctx);
flint_printf("near: "); gr_println(y, ctx);
status |= decfloat_set_round(y, x, 2, DECIMAL_RND_NEAR_AWAY, ctx);
flint_printf("near_away: "); gr_println(y, ctx);
status |= decfloat_set_round(y, x, 2, DECIMAL_RND_FLOOR, ctx);
flint_printf("floor: "); gr_println(y, ctx);
/* a sum of operands with very different exponents costs O(prec) */
status |= decfloat_set_ui(x, 1, ctx);
status |= decfloat_set_str(y, "1e-1000000000000", ctx);
status |= decfloat_add_round(z, x, y, 10, DECIMAL_RND_UP, ctx);
flint_printf("1 + 1e-10^12, up: "); gr_println(z, ctx);
status |= decfloat_add_round(z, x, y, 10, DECIMAL_RND_NEAR, ctx);
flint_printf("1 + 1e-10^12, near: "); gr_println(z, ctx);
/* the rounding error of 1/7 at 10 digits */
status |= decfloat_set_ui(x, 1, ctx);
status |= decfloat_div_ui(y, x, 7, ctx);
status |= decfloat_set_round_info(z, y, 3, DECIMAL_RND_NEAR, &info, err, ctx);
flint_printf("1/7 = "); gr_println(y, ctx);
flint_printf("to 3 digits: "); gr_println(z, ctx);
flint_printf("inexact %d, increased %d, error <= ", info.inexact, info.increased);
{
char * t = _decmag_get_str(err, ctx);
flint_printf("%s\n", t);
flint_free(t);
}
flint_printf("status %d\n", status);
decfloat_clear(x, ctx);
decfloat_clear(y, ctx);
decfloat_clear(z, ctx);
_decmag_clear(err, ctx);
gr_ctx_clear(ctx);
return 0;
}near: 0.12 near_away: 0.13 floor: 0.12 1 + 1e-10^12, up: 1.000000001 1 + 1e-10^12, near: 1 1/7 = 0.1428571429 to 3 digits: 0.143 inexact 1, increased 1, error <= 1.429e-4 status 0
Guides#
The FLINT team, “Correctly rounded arithmetic (decimal.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/decimal/arithmetic.html (generated 2026-10-20).
@misc{flint-doc-decimal-arithmetic,
author = {{The FLINT team}},
title = {{Correctly rounded arithmetic (decimal.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/decimal/arithmetic.html},
note = {Generated 2026-10-20}
}