bin_float design

bin_float implements IEEE 754 binary floating-point arithmetic at any precision. This page explains the mathematics behind it: the value set, the rounding functions and the error model they satisfy, how each operation decides the correctly rounded result from exact integer data, how the exponent range, tininess and the status flags are handled, why the IEEE remainder is exact, how decimal conversion and the elementary functions are certified, and why the fast integer kernels cannot change a result. The API reference lists the callable surface and the tutorial shows it in use.

Design goal

Every operation of bin_float returns the value that IEEE 754-2019 requires of a correctly rounded operation, ∘(f(x))\circ(f(x)) for the exact real f(x)f(x), together with exactly the IEEE status flags, for every precision pp from 1 to 2282^{28} bits and every exponent range up to ±(230−1)\pm(2^{30}-1). The same code serves two audiences: arbitrary-precision numerics that need dyadic values far beyond Double, and bit-exact emulation of binary16, binary32, binary64 and binary128 including subnormals, the five rounding directions and both tininess rules. There is no hidden state: precision, rounding and range travel in an immutable BinaryContext, and flags travel back as a value.

Mathematical background

Dyadic values and the stored triple

A finite BinFloat denotes the dyadic rational

x=(−1)s⋅c⋅2e,s∈{0,1}, c∈N, e∈Z,x = (-1)^s \cdot c \cdot 2^{e}, \qquad s \in \{0, 1\},\ c \in \mathbb{N},\ e \in \mathbb{Z},

where cc is a BinCoeff and ee is exponent2(). The representation is canonical: if c≠0c \ne 0 then cc is odd, and if c=0c = 0 then e=0e = 0. Every dyadic rational has exactly one such form (factor out 2ν2(c)2^{\nu_2(c)}), so two finite values are numerically equal exactly when their signs (for nonzero values), coefficients and exponents agree. The exponent of the leading bit is

top⁡(x)=⌊log⁡2∣x∣⌋=e+bits⁡(c)−1,\operatorname{top}(x) = \lfloor \log_2 |x| \rfloor = e + \operatorname{bits}(c) - 1,

and every comparison, range check and rounding decision below is phrased in terms of top⁡\operatorname{top} and bits⁡\operatorname{bits} instead of floating logarithms. Each value also carries a precision pp with bits⁡(c)≤p\operatorname{bits}(c) \le p; it records the format the value belongs to and is the default precision of plain operations on it.

The IEEE 754 binary formats

A binary interchange format with kk bits has a sign bit, a ww-bit biased exponent field EE and a (p−1)(p-1)-bit trailing significand field TT (IEEE 754-2019 clause 3.4).11 IEEE Std 754-2019, IEEE Standard for Floating-Point Arithmetic: clause 3 (formats), 4.3 (rounding-direction attributes), 5 (operations), 6 (infinities, NaNs, signed zero), 7 (default exception handling). With emax⁡=2w−1−1e_{\max} = 2^{w-1} - 1, the bias emax⁡e_{\max} and emin⁡=1−emax⁡e_{\min} = 1 - e_{\max}, an encoding means

v={(−1)s 2E−emax⁡(1+T 21−p)1≤E≤2w−2(normal),(−1)s 2emin⁡(0+T 21−p)E=0(subnormal or zero),(−1)s ∞E=2w−1, T=0,NaNE=2w−1, T≠0.v = \begin{cases} (-1)^s\, 2^{E - e_{\max}} \bigl(1 + T\, 2^{1-p}\bigr) & 1 \le E \le 2^w - 2 \quad\text{(normal)},\\ (-1)^s\, 2^{e_{\min}} \bigl(0 + T\, 2^{1-p}\bigr) & E = 0 \quad\text{(subnormal or zero)},\\ (-1)^s\, \infty & E = 2^w - 1,\ T = 0,\\ \mathrm{NaN} & E = 2^w - 1,\ T \ne 0. \end{cases}
Formatkkwwppemax⁡e_{\max}emin⁡e_{\min}largest Ω\Omegasmallest normalsmallest subnormal
binary161651115−1465504655042−142^{-14}2−242^{-24}
binary3232824127−126(2−2−23)2127(2-2^{-23})2^{127}2−1262^{-126}2−1492^{-149}
binary646411531023−1022(2−2−52)21023(2-2^{-52})2^{1023}2−10222^{-1022}2−10742^{-1074}
binary1281281511316383−16382(2−2−112)216383(2-2^{-112})2^{16383}2−163822^{-16382}2−164942^{-16494}

Forgetting the encoding, the finite values of the format are

F(p,emin⁡,emax⁡)={ M⋅2q:M∈Z, ∣M∣<2p, q≥emin⁡−p+1, ∣M∣2q<2emax⁡+1 }.F(p, e_{\min}, e_{\max}) = \{\, M \cdot 2^{q} : M \in \mathbb{Z},\ |M| < 2^{p},\ q \ge e_{\min} - p + 1,\ |M| 2^{q} < 2^{e_{\max}+1} \,\}.

A BinaryContext is exactly this triple plus a rounding direction and a tininess rule. Its emin⁡e_{\min} and emax⁡e_{\max} are leading-bit exponents, so a normal number has emin⁡≤top⁡(x)≤emax⁡e_{\min} \le \operatorname{top}(x) \le e_{\max}, and the grid below 2emin⁡2^{e_{\min}} has the fixed quantum

η=2emin⁡−p+1,\eta = 2^{e_{\min} - p + 1},

the smallest positive subnormal. Missing bounds are replaced by the implementation range ±(230−1)\pm(2^{30}-1), which is large enough that every exponent arithmetic step fits a 64-bit intermediate and every stored exponent an Int; binary_precision_max =228= 2^{28} keeps emin⁡−p+1e_{\min} - p + 1 in range too.

Rounding functions

For x∈Rx \in \mathbb{R} let x−=max⁡{y∈F:y≤x}x^- = \max\{y \in F : y \le x\} and x+=min⁡{y∈F:y≥x}x^+ = \min\{y \in F : y \ge x\}, extending FF by ±∞\pm\infty beyond ±Ω\pm\Omega for the moment. The six rounding directions of BinaryRoundingMode are the maps R→F∪{±∞}\mathbb{R} \to F \cup \{\pm\infty\}

RD⁡(x)=x−,RU⁡(x)=x+,RZ⁡(x)=sign⁡(x) ∣x∣−,RA⁡(x)=sign⁡(x) ∣x∣+,RNE⁡(x)=the nearer of x−,x+, the one with even M on a tie,RNA⁡(x)=the nearer of x−,x+, the one of larger magnitude on a tie,\begin{aligned} \operatorname{RD}(x) &= x^-, \qquad \operatorname{RU}(x) = x^+, \\ \operatorname{RZ}(x) &= \operatorname{sign}(x)\,|x|^-, \qquad \operatorname{RA}(x) = \operatorname{sign}(x)\,|x|^+, \\ \operatorname{RNE}(x) &= \text{the nearer of } x^-, x^+, \text{ the one with even } M \text{ on a tie}, \\ \operatorname{RNA}(x) &= \text{the nearer of } x^-, x^+, \text{ the one of larger magnitude on a tie}, \end{aligned}

where RA (RoundAwayFromZero) is not an IEEE attribute but the GDA “round-up” mode that @lf_arith.RoundingMode shares with the decimal cores. Two properties of every ∘\circ above carry most of the proofs on this page:

(R1)  x∈F  ⟹  ∘(x)=x,(R2)  x≤y  ⟹  ∘(x)≤∘(y).\text{(R1)}\ \ x \in F \implies \circ(x) = x, \qquad\qquad \text{(R2)}\ \ x \le y \implies \circ(x) \le \circ(y).

(R1) holds because x−=x+=xx^- = x^+ = x on FF. (R2) holds because each ∘(x)\circ(x) is one of the two neighbours of xx chosen by a rule that only moves from x−x^- to x+x^+ as xx increases through a cell [x−,x+][x^-, x^+].

The standard error model

Let u=2−pu = 2^{-p} be the unit roundoff. Take xx with 2t≤∣x∣<2t+12^{t} \le |x| < 2^{t+1} and t≥emin⁡t \ge e_{\min} (the normal range). The points of FF in that binade are spaced 2t−p+12^{t-p+1} apart, so

∣RN⁡(x)−x∣≤12 2t−p+1=2t−p≤2−p∣x∣=u∣x∣,∣RD⁡(x)−x∣, ∣RU⁡(x)−x∣<2t−p+1≤2u∣x∣,\begin{aligned} |\operatorname{RN}(x) - x| &\le \tfrac12\, 2^{t-p+1} = 2^{t-p} \le 2^{-p} |x| = u|x|, \\ |\operatorname{RD}(x) - x|,\ |\operatorname{RU}(x) - x| &< 2^{t-p+1} \le 2u|x|, \end{aligned}

where RN is RNE or RNA. Writing ∘(x)=x(1+δ)\circ(x) = x(1 + \delta) gives the standard model

fl⁡(a∘b)=(a∘b)(1+δ),∣δ∣≤u (nearest),∣δ∣<2u (directed).\operatorname{fl}(a \circ b) = (a \circ b)(1 + \delta), \qquad |\delta| \le u \ \text{(nearest)}, \quad |\delta| < 2u \ \text{(directed)}.

The nearest bound sharpens to ∣δ∣≤u/(1+u)|\delta| \le u/(1+u) by dividing by ∣∘(x)∣|\circ(x)| instead of ∣x∣|x|.22 N. J. Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., SIAM 2002, §2.2; D. Goldberg, “What every computer scientist should know about floating-point arithmetic”, ACM Computing Surveys 23(1), 1991. Below 2emin⁡2^{e_{\min}} the spacing is the constant η\eta, so the error is absolute: ∣RN⁡(x)−x∣≤η/2|\operatorname{RN}(x) - x| \le \eta/2 and ∣RD⁡(x)−x∣<η|\operatorname{RD}(x) - x| < \eta. Both regimes together give the model with an underflow term,

fl⁡(a∘b)=(a∘b)(1+δ)+ϵ,∣δ∣≤u,∣ϵ∣≤η2,δϵ=0,\operatorname{fl}(a \circ b) = (a \circ b)(1 + \delta) + \epsilon, \qquad |\delta| \le u,\quad |\epsilon| \le \tfrac{\eta}{2},\quad \delta\epsilon = 0,

for nearest rounding (2u2u and η\eta for directed rounding). Addition and subtraction never need ϵ\epsilon: if a,b∈Fa, b \in F then both are integer multiples of η\eta, so is a±ba \pm b, and a multiple of η\eta below 2emin⁡2^{e_{\min}} lies in FF. Hence a subnormal sum is exact, which is why gradual underflow keeps a−b=0  ⟺  a=ba - b = 0 \iff a = b.33 J.-M. Muller et al., Handbook of Floating-Point Arithmetic, 2nd ed., Birkhäuser 2018, §2.1 and §4.3.

Correct rounding is a stronger property than the model: the result is the one point ∘(f(x))\circ(f(x)), not merely some point within uu. All of bin_float is built to return that point, so the model above holds for every operation, including the elementary functions.

Design decisions

Round once from exact data

Problem. A result must equal ∘(r)\circ(r) for the exact real rr, but rr may need far more bits than pp (a product of two pp-bit numbers has 2p2p), or infinitely many (a quotient, a square root, exe^x).

Options. Compute in a wider format and round again, as hardware without FMA does; or keep a fixed number of guard bits; or decide the rounding from exact information.

Choice. Every operation computes an exact description of rr and calls one finalizer. For dyadic results (sum, difference, product, fma, scaleb, remainder, conversion) the description is the exact integer magnitude mm and exponent ee with r=±m2er = \pm m 2^{e}. The finalizer picks the shift

σ=max⁡(bits⁡(m)−p, (emin⁡−p+1)−e, 0),\sigma = \max\bigl(\operatorname{bits}(m) - p,\ (e_{\min} - p + 1) - e,\ 0\bigr),

the larger of the precision shift and the shift onto the subnormal grid, and splits m2−σ=q+fm 2^{-\sigma} = q + f with q=⌊m2−σ⌋q = \lfloor m 2^{-\sigma} \rfloor and 0≤f<10 \le f < 1 into three items of data:

q,g=[ f≥12 ]=bit σ−1 of m,t=[ f∉{0,12} ]=[ ν2(m)<σ−1 ].q, \qquad g = [\,f \ge \tfrac12\,] = \text{bit } \sigma - 1 \text{ of } m, \qquad t = [\,f \notin \{0, \tfrac12\}\,] = [\,\nu_2(m) < \sigma - 1\,].

These are the classical round and sticky bits, read from the coefficient by test_bit and ctz without building any shifted copy. They determine every rounding direction: with q0q_0 the last bit of qq,

Directionincrement qq when
RNEg∧(t∨q0)g \wedge (t \vee q_0)
RNAgg
RZnever
RU(g∨t)∧s=0(g \vee t) \wedge s = 0
RD(g∨t)∧s=1(g \vee t) \wedge s = 1
RAg∨tg \vee t

Derivation. f>12  ⟺  g∧tf > \frac12 \iff g \wedge t, f=12  ⟺  g∧¬tf = \frac12 \iff g \wedge \neg t, and f>0  ⟺  g∨tf > 0 \iff g \vee t. RNE rounds the magnitude up when f>12f > \frac12, or when f=12f = \frac12 and qq is odd; that is (g∧t)∨(g∧¬t∧q0)=g∧(t∨q0)(g \wedge t) \vee (g \wedge \neg t \wedge q_0) = g \wedge (t \vee q_0). The directed rows round the magnitude up exactly when f>0f > 0 and the direction points away from zero for the sign ss. Inexactness is g∨tg \vee t.

Why. Because σ\sigma already includes the subnormal shift, a tiny result is rounded once, directly from mm, to the grid ηZ\eta \mathbb{Z}. Rounding first to pp bits and then to the subnormal grid would be a double rounding: a value just above a midpoint of the coarse grid can be pushed onto that midpoint by the first rounding and then rounded the wrong way by the second. A carry out of qq (when q+1=2pq + 1 = 2^p) only raises top⁡\operatorname{top} by one; the result is re-normalized and the overflow test below is applied to the rounded value.

Division from quotient and remainder

Problem. a/ba/b with a=ca2eaa = c_a 2^{e_a}, b=cb2ebb = c_b 2^{e_b} is a rational N/D⋅2eN/D \cdot 2^{e} (N=caN = c_a, D=cbD = c_b, e=ea−ebe = e_a - e_b) whose binary expansion is usually infinite.

Choice. First the exact leading exponent: with k=bits⁡(N)−bits⁡(D)k = \operatorname{bits}(N) - \operatorname{bits}(D), ⌊log⁡2(N/D)⌋\lfloor \log_2 (N/D) \rfloor is kk if N≥D2kN \ge D 2^{k} and k−1k - 1 otherwise, one integer comparison. That fixes the target exponent τ=max⁡(top⁡−p+1, emin⁡−p+1)\tau = \max(\operatorname{top} - p + 1,\ e_{\min} - p + 1), and then one integer division

N2e−τ=qD+r,0≤r<DN 2^{e - \tau} = q D + r, \qquad 0 \le r < D

gives qq directly, and the remainder gives the rounding data: f=r/Df = r/D, so

g=[ 2r≥D ],t=[ r≠0∧2r≠D ].g = [\,2r \ge D\,], \qquad t = [\,r \ne 0 \wedge 2r \ne D\,].

No approximation of N/DN/D is involved; the rounding is decided by the sign of 2r−D2r - D. When e−τ<0e - \tau < 0 the shift is moved to the denominator, and a quotient below one unit is decided by comparing 2N2N with D2τ−eD 2^{\tau - e} without forming it.

Square root from an integer root and a midpoint test

Problem. c2e\sqrt{c 2^{e}} is irrational unless c2ec 2^{e} is a square.

Choice. The leading exponent is ⌊top⁡(x)/2⌋\lfloor \operatorname{top}(x)/2 \rfloor (floor division), which fixes τ\tau as above. Write the radicand as X=c 2e−2τX = c\, 2^{e - 2\tau}, so x=X 2τ\sqrt{x} = \sqrt{X}\, 2^{\tau}. An exact integer square root gives s=⌊X⌋s = \lfloor \sqrt{X} \rfloor with remainder X−s2X - s^2; the root is exact iff the remainder is zero. Otherwise the rounding data come from the midpoint s+12s + \frac12:

X≷s+12  ⟺  X≷(s+12)2  ⟺  4X≷(2s+1)2,\sqrt{X} \gtrless s + \tfrac12 \iff X \gtrless \bigl(s + \tfrac12\bigr)^2 \iff 4X \gtrless (2s+1)^2,

an exact comparison of integers (with the power of two moved to the other side when XX is fractional). So g=[4X≥(2s+1)2]g = [4X \ge (2s+1)^2] and t=¬exact∧[4X≠(2s+1)2]t = \neg\text{exact} \wedge [4X \ne (2s+1)^2]. When XX is an integer the right side is odd and the left even, so the root is never exactly a midpoint; this is the classical fact that x\sqrt{x} of a pp-bit number is never a (p+1)(p+1)-bit midpoint.44 Muller et al., Handbook of Floating-Point Arithmetic, §5.3 and §7.6. The equality branch is still kept, because an operand with more bits than the context precision can make X\sqrt{X} an exact midpoint (for example 9/4=3/2\sqrt{9/4} = 3/2 rounded to one bit).

Exponent range, overflow and underflow

Overflow is decided on the rounded value: if the rounded result has top⁡>emax⁡\operatorname{top} > e_{\max}, the operation overflows, which is IEEE 754’s “after rounding” rule (clause 7.4). From the rounding table this happens at the thresholds

RNE, RNA:∣r∣≥2emax⁡(2−2−p)=Ω+12ulp⁡(Ω),RA, and RU for r>0, RD for r<0:∣r∣>Ω,RZ, and RU for r<0, RD for r>0:never to ∞,\begin{aligned} \text{RNE, RNA:}\quad & |r| \ge 2^{e_{\max}}\bigl(2 - 2^{-p}\bigr) = \Omega + \tfrac12 \operatorname{ulp}(\Omega), \\ \text{RA, and RU for } r>0, \text{ RD for } r<0:\quad & |r| > \Omega, \\ \text{RZ, and RU for } r<0, \text{ RD for } r>0:\quad & \text{never to } \infty, \end{aligned}

because a value in (Ω,Ω+12ulp⁡)(\Omega, \Omega + \frac12\operatorname{ulp}) rounds to nearest down to Ω\Omega, while the tie Ω+12ulp⁡\Omega + \frac12\operatorname{ulp} goes to the even neighbour 2emax⁡+12^{e_{\max}+1}, which lies outside FF. An overflowing result is ±∞\pm\infty for the first two groups and ±Ω\pm\Omega for the third, always with overflow and inexact. In binary16, Ω=65504\Omega = 65504 and 12ulp⁡(Ω)=16\frac12\operatorname{ulp}(\Omega) = 16:

///|
test "binary16 overflow threshold under nearest rounding" {
  let ctx = @bin_float.BinaryContext::binary16()
  let (below, below_flags) = @bin_float.BinFloat::from_int(65519).round_ctx(ctx)
  let (at, at_flags) = @bin_float.BinFloat::from_int(65520).round_ctx(ctx)
  inspect("\{below} \{below_flags.overflow()}", content="2047p5 false")
  inspect("\{at} \{at_flags.overflow()}", content="inf true")
}

Tininess. A nonzero result is tiny when it lies strictly between ±2emin⁡\pm 2^{e_{\min}}. IEEE 754-2019 (clause 7.5) allows two readings, and TininessDetection selects one:

before rounding: ∣r∣<2emin⁡;after rounding: ∣∘p,∞(r)∣<2emin⁡,\text{before rounding: } |r| < 2^{e_{\min}}; \qquad \text{after rounding: } |\circ_{p,\infty}(r)| < 2^{e_{\min}},

where ∘p,∞\circ_{p,\infty} rounds to pp bits with an unbounded exponent range. The finalizer computes top⁡(r)\operatorname{top}(r) exactly and, for the after-rounding rule, a second split of the same magnitude at the precision shift alone. The two readings differ only for rr just below 2emin⁡2^{e_{\min}} that rounds up to it at pp bits: for binary16, r=2−14−2−27r = 2^{-14} - 2^{-27} is tiny before rounding but rounds at 11 bits to 2−142^{-14}, which is not tiny.

Underflow flag. Under default exception handling the underflow flag is raised for a tiny result only when it is also inexact. The finalizer returns no flags at all for an exact result, so an exact subnormal (for example any subnormal difference, as shown above) raises nothing. The binary16 product 2−14×(1−2−11)2^{-14} \times (1 - 2^{-11}) shows the full rule: the exact value 2−14−2−252^{-14} - 2^{-25} is tiny under both readings, lies halfway between two subnormals of spacing 2−242^{-24}, rounds to the even neighbour 2−142^{-14}, the smallest normal, and raises underflow and inexact, although the encoded result 0x0400 is normal.

///|
test "underflow is raised for a tiny inexact result that rounds to normal" {
  let format = @bin_float.BinaryInterchangeFormat::Binary16
  let smallest_normal = @bin_float.BinaryInterchange::from_hex("0400", format)
    .unwrap()
    .to_bin_float()
  let below_one = @bin_float.BinaryInterchange::from_hex("3BFF", format)
    .unwrap()
    .to_bin_float()
  let (product, flags) = smallest_normal.mul_ctx(below_one, format.context())
  inspect(product.to_interchange(format).0.to_hex(), content="0400")
  inspect("\{flags.underflow()} \{flags.inexact()}", content="true true")
}

Far below the range. A result certainly smaller than η/2\eta/2 in magnitude (decided from exponent bounds without forming it, for example 2−109⋅2−1092^{-10^9} \cdot 2^{-10^9}) rounds to ±0\pm 0 or ±η\pm\eta according to the direction, with underflow and inexact. A result certainly above the range goes to the overflow result.

Signed zeros and NaNs

An exact zero sum a+b=0a + b = 0 with a,ba, b of opposite sign is +0+0 in every direction except RD, where it is −0-0; (−0)+(−0)=−0(-0) + (-0) = -0 (clause 6.3). Products and quotients take the exclusive-or of the signs. A NaN operand yields the first NaN operand, quieted, with its sign and payload (clause 6.2.3 allows any input NaN), and invalid_operation is raised exactly when an operand is a signaling NaN or the operation is invalid on its own (∞−∞\infty - \infty, 0⋅∞0 \cdot \infty, 0/00/0, ∞/∞\infty/\infty, x<0\sqrt{x<0}, remainder⁡(∞,y)\operatorname{remainder}(\infty, y), remainder⁡(x,0)\operatorname{remainder}(x, 0)). Flags are values: combine is the bitwise OR, so the flags of a computation form a commutative idempotent monoid and can be accumulated in any order, which a global sticky register cannot offer to concurrent code.

Far-apart operands in addition

Problem. 2109+2−1092^{10^9} + 2^{-10^9} is exact as a dyadic number, but forming it needs a two-billion-bit coefficient.

Choice. When the leading exponents differ by more than p+3p + 3, the smaller operand is truncated at position exp⁡(high)−p−3\operatorname{exp}(\text{high}) - p - 3 and everything below is replaced by one sticky bit: the integer part LL of the truncated low operand enters exactly, and if anything was discarded the magnitude becomes 2(H±L)+12(H \pm L) + 1 (for subtraction 2(H−L−1)+12(H - L - 1) + 1) at half the unit.

Why it is exact for rounding. The result has top⁡≥top⁡(high)−1\operatorname{top} \ge \operatorname{top}(\text{high}) - 1, so the rounding position is at least top⁡(high)−p\operatorname{top}(\text{high}) - p and the round bit at least one below it, while every discarded bit lies at or below top⁡(high)−p−3\operatorname{top}(\text{high}) - p - 3. The discarded part therefore changes neither qq nor gg, only whether tt is set, and the substitute bit sets tt exactly when something nonzero was discarded. For subtraction, H−(L+ε)=(H−L−1)+(1−ε)H - (L + \varepsilon) = (H - L - 1) + (1 - \varepsilon) with 0<1−ε<10 < 1 - \varepsilon < 1, so the same substitution applies to the borrowed form. The complete argument, including the subnormal shift, is in the attachment below.

Fused multiply-add

fma_ctx forms the product cxcy2ex+eyc_x c_y 2^{e_x + e_y} exactly as a value whose precision equals its own bit length, and passes it to the addition finalizer, so xy+zx y + z is rounded once (clause 5.4.1). The difference from two roundings is the point of the operation: for a=RN⁡(0.1)a = \operatorname{RN}(0.1) in binary64, RN⁡(a⋅a)−a⋅a\operatorname{RN}(a \cdot a) - a \cdot a is lost by mul_ctx followed by sub_ctx (the second operation sees two equal numbers) but fma_ctx(a, a, -RN(a·a)) returns it exactly, −8.33…⋅10−19-8.33\ldots \cdot 10^{-19}. That the result is exact is Dekker’s theorem: the error of a rounded product is itself in FF when no underflow occurs.55 T. J. Dekker, “A floating-point technique for extending the available precision”, Numerische Mathematik 18, 1971; Muller et al., §4.4. If the product exponent leaves the Int range, the product either certainly overflows, or it is so small that it acts as a sticky bit next to a nonzero addend; the code places a single bit p+8p + 8 positions below the addend’s last bit, which by the far-operand argument above rounds identically.

IEEE remainder is exact

Claim. If x,y∈Fx, y \in F (same precision pp, same range) and y≠0y \ne 0, then r=x−nyr = x - n y with n=RNE⁡Z(x/y)n = \operatorname{RNE}_{\mathbb{Z}}(x/y) lies in FF.

Proof. Write x=Mx2qxx = M_x 2^{q_x}, y=My2qyy = M_y 2^{q_y} with ∣Mx∣,∣My∣<2p|M_x|, |M_y| < 2^p and qx,qy≥emin⁡−p+1q_x, q_y \ge e_{\min} - p + 1. By the choice of nn, ∣r∣≤∣y∣/2|r| \le |y|/2. If n=0n = 0 then r=xr = x. Otherwise ∣x/y∣≥12|x/y| \ge \frac12, so ∣r∣≤∣y∣/2≤∣x∣|r| \le |y|/2 \le |x|. Now rr is an integer multiple of 2min⁡(qx,qy)2^{\min(q_x, q_y)}.

  • If qx≥qyq_x \ge q_y: r=k2qyr = k 2^{q_y} with ∣k∣2qy≤∣My∣2qy/2|k| 2^{q_y} \le |M_y| 2^{q_y}/2, so ∣k∣<2p−1|k| < 2^{p-1}.
  • If qx<qyq_x < q_y: r=k2qxr = k 2^{q_x} with ∣k∣2qx≤∣x∣=∣Mx∣2qx|k| 2^{q_x} \le |x| = |M_x| 2^{q_x}, so ∣k∣<2p|k| < 2^{p}.

In both cases ∣k∣<2p|k| < 2^p, the exponent is at least emin⁡−p+1e_{\min} - p + 1, and ∣r∣≤max⁡(∣x∣,∣y∣)≤Ω|r| \le \max(|x|, |y|) \le \Omega, so r∈Fr \in F. □\square

The implementation never forms nn, which can have 2302^{30} bits. With X=∣x∣2−mX = |x| 2^{-m}, Y=∣y∣2−mY = |y| 2^{-m}, m=min⁡(qx,qy)m = \min(q_x, q_y) (integers), it computes X mod 2YX \bmod 2Y by modular exponentiation of 2qx−qy2^{q_x - q_y} when qx>qyq_x > q_y. Writing X=Q(2Y)+RX = Q (2Y) + R, 0≤R<2Y0 \le R < 2Y, gives ⌊X/Y⌋=2Q+[R≥Y]\lfloor X/Y \rfloor = 2Q + [R \ge Y], so RR alone yields both X mod YX \bmod Y and the parity of ⌊X/Y⌋\lfloor X/Y \rfloor, which is all the ties-to-even choice of nn needs. The exact rr then goes through the usual finalizer; by the claim, no rounding happens for operands of the context’s format.

Neighbours, scaling and integral values

next_up_ctx(x) adds a positive step 2π−22^{\pi - 2} to xx and rounds toward +∞+\infty, where π=min⁡(emin⁡−p, e(x), top⁡(x)−p)\pi = \min(e_{\min} - p,\ e(x),\ \operatorname{top}(x) - p). Every gap between consecutive points of FF next to xx is at least 2min⁡(emin⁡−p+1, top⁡(x)−p)2^{\min(e_{\min} - p + 1,\ \operatorname{top}(x) - p)} and xx itself is a multiple of 2e(x)2^{e(x)}, so x<x+2π−2<x+x < x + 2^{\pi - 2} < x^{+} for x∈Fx \in F, and by the definition of RU the result is the least point of FF above xx. The same argument works for an xx with more than pp bits, which is why such operands are accepted. The flags of this internal addition are discarded, because nextUp is quiet (clause 5.3.1) even when it steps from Ω\Omega to +∞+\infty.

scaleb_ctx(x, n) is the finalizer applied to (c,e+n)(c, e + n): exact in the normal range, correctly rounded with underflow and overflow outside it. logb_ctx returns top⁡(x)\operatorname{top}(x), which is exact and correct for subnormal xx because top⁡\operatorname{top} is computed on the integer coefficient. The integral roundings use the same round and sticky bits with σ=−e\sigma = -e (the bits below the binary point); to_int_ctx and its siblings round first and then compare the integer with the target range, reporting invalid_operation instead of returning an implementation-defined sentinel.

Decimal conversion

Parsing. from_string_ctx reads D⋅10kD \cdot 10^{k} exactly (DD an integer without trailing zeros). For ∣k∣|k| up to max⁡(400,⌊(3n+p)/2⌋+64)\max\bigl(400, \lfloor (3n + p)/2 \rfloor + 64\bigr) (nn the number of digits) it is rounded exactly: D⋅5k⋅2kD \cdot 5^{k} \cdot 2^{k} by the dyadic finalizer for k≥0k \ge 0, and D/5∣k∣⋅2kD / 5^{|k|} \cdot 2^{k} by the division finalizer for k<0k < 0. Beyond that bound it uses directed enclosures [RD⁡w(D)RD⁡w(10k),RU⁡w(D)RU⁡w(10k)][\operatorname{RD}_w(D)\operatorname{RD}_w(10^k), \operatorname{RU}_w(D)\operatorname{RU}_w(10^k)] at a working precision ww, doubled until both ends round to the same value with the same flags. The bound makes the loop terminate. The rounding breakpoints of a direction are the points of FF (directed modes) or the midpoints between them (nearest modes); both are dyadic with at most p+1p + 1 significant bits. For k≥0k \ge 0 the odd part of D10kD 10^{k} is a multiple of 5k5^{k}, and 5k>22.32k>2p+25^{k} > 2^{2.32 k} > 2^{p+2} beyond the bound, so the value is no breakpoint. For k<0k < 0, 5∣k∣>10n>D5^{|k|} > 10^{n} > D beyond the bound, so 5∣k∣∤D5^{|k|} \nmid D and D/10∣k∣D/10^{|k|} is not even dyadic. A value that is no breakpoint has a positive distance to every breakpoint, and the enclosure width tends to zero as ww grows, so some ww certifies it. Values whose binary logarithm is certainly beyond the range, estimated with log⁡210\log_2 10 and a safety margin, overflow or underflow without any arithmetic, so 1e100000000 costs nothing.

Fixed digits. to_decimal_string_ctx(x, d) needs round⁡(x/10E−d+1)\operatorname{round}(x / 10^{E - d + 1}) with E=⌊log⁡10∣x∣⌋E = \lfloor \log_{10}|x| \rfloor. EE starts from ⌊top⁡(x)log⁡102⌋\lfloor \operatorname{top}(x) \log_{10} 2 \rfloor, which is within one of the answer, and is corrected by comparing ∣x∣|x| with 10E+110^{E+1}, first through directed bounds of the power and, when they straddle, exactly. The quotient is formed exactly as an integer division when its operands have at most 2202^{20} bits, so ties and exact results are recognised; otherwise directed enclosures are widened until both ends round to the same integer, which terminates because such a quotient is neither an integer nor a half-integer. A carry into a new leading digit (9.99→10.09.99 \to 10.0) increments EE and repeats.

Shortest output. Let I(x)I(x) be the set of reals that round to xx under RNE in the context; it is an interval containing xx. For each digit count nn, the two nn-digit decimals next to xx (truncated and rounded away from zero) are candidates, and a candidate is accepted when parsing it returns xx, that is when it lies in I(x)I(x). Acceptance is monotone in nn: if the nn-digit truncation dnd_n lies in I(x)I(x), the (n+1)(n+1)-digit truncation satisfies dn≤dn+1≤xd_n \le d_{n+1} \le x (in magnitude), so it lies in the interval too, and likewise for the upper neighbour. So the least accepted nn is found by bisection on [1,⌈plog⁡102⌉+2][1, \lceil p \log_{10} 2 \rceil + 2], an upper bound at which a candidate is always accepted.66 The digit count ⌈plog⁡102⌉+1\lceil p \log_{10} 2 \rceil + 1 suffices for round trip (Matula 1968; Goldberg 1991, Theorem 15); the extra digit is a margin for the bisection’s upper end. If both candidates are accepted, the nearer one is chosen, then the even one, which is the nearest decimal of that length. For binary64 this reproduces the host formatter on every value tested.

Certified elementary functions

Problem. For f=exp⁡,ln⁡,sin⁡,…f = \exp, \ln, \sin, \ldots the value f(x)f(x) is transcendental and must be rounded correctly without knowing it.

Options. Fixed polynomial approximations with a proven error bound (fast, but tied to one precision); Ziv’s strategy of evaluating with an a priori error bound and retrying at a higher precision when the rounding is ambiguous;77 A. Ziv, “Fast evaluation of elementary mathematical functions with correctly rounded last bit”, ACM TOMS 17(3), 1991; for interval evaluation see W. Tucker, Validated Numerics, Princeton 2011, and F. Johansson, “Arb: efficient arbitrary-precision midpoint-radius interval arithmetic”, IEEE Trans. Computers 66(8), 2017. or interval evaluation.

Choice. A Ziv loop whose error bound is not estimated but computed: every elementary function evaluates an enclosure [L,U]∋f(x)[L, U] \ni f(x) in which every internal operation is rounded downward for LL and upward for UU at a working precision ww. If

∘(L)=∘(U)andflags⁡(L)=flags⁡(U),\circ(L) = \circ(U) \quad\text{and}\quad \operatorname{flags}(L) = \operatorname{flags}(U),

the common value is returned, otherwise ww grows. This is sound by (R2): L≤f(x)≤UL \le f(x) \le U implies ∘(L)≤∘(f(x))≤∘(U)\circ(L) \le \circ(f(x)) \le \circ(U), so equal ends force ∘(f(x))=∘(L)\circ(f(x)) = \circ(L). The flags agree as well, because overflow, tininess and inexactness are monotone in the same way on one side of zero; the test compares them explicitly instead of relying on this.

The enclosures come from series with rigorous tails and from monotone reductions. For exp⁡\exp on [0,1/8][0, 1/8], the terms tk=xk/k!t_k = x^k/k! satisfy tk+1/tk=x/(k+1)≤1/8t_{k+1}/t_k = x/(k+1) \le 1/8, so after the last summed term tnt_n

∑j>ntj≤tn∑i≥18−i=tn7≤2 tn,\sum_{j > n} t_j \le t_n \sum_{i \ge 1} 8^{-i} = \frac{t_n}{7} \le 2\, t_n,

and the code stops once tn<2−(w+8)t_n < 2^{-(w+8)} and adds 2tn2 t_n (rounded upward) to the upper sum; the lower sum of positive terms is already a lower bound. Larger arguments are halved rr times and the result squared rr times, which is monotone on positive numbers and therefore keeps the enclosure; e−x=1/exe^{-x} = 1/e^{x} handles negative arguments. For ln⁡v\ln v with v∈[1,2]v \in [1, 2] the series is

ln⁡v=2artanh⁡z=2∑k≥0z2k+12k+1,z=v−1v+1∈[0,13],\ln v = 2 \operatorname{artanh} z = 2 \sum_{k \ge 0} \frac{z^{2k+1}}{2k+1}, \qquad z = \frac{v - 1}{v + 1} \in \bigl[0, \tfrac13\bigr],

with tail ∑k≥mz2k+1/(2k+1)≤z2m+12m+1⋅11−z2\sum_{k \ge m} z^{2k+1}/(2k+1) \le \frac{z^{2m+1}}{2m+1} \cdot \frac{1}{1 - z^2}, the bound the code adds. The trigonometric functions reduce xx by an enclosure of π/2\pi/2 (from π=4arctan⁡1\pi = 4 \arctan 1, itself enclosed by the arctangent series after argument halving) at w≥p+max⁡(0,top⁡(x)+1)+96w \ge p + \max(0, \operatorname{top}(x) + 1) + 96 bits, so the quadrant k=round⁡(x/(π/2))k = \operatorname{round}(x / (\pi/2)) is the same integer at both ends of the enclosure; otherwise the attempt is repeated at a higher precision. This is the Payne–Hanek idea realised by brute precision instead of a stored table of 2/π2/\pi; its cost grows with log⁡2∣x∣\log_2|x|, so inputs needing more than 10610^6 bits are refused with ResourceLimit rather than run for minutes.

Budget. The loop starts at w0=p+64w_0 = p + 64 and steps wi+1=wi+max⁡(32,⌊wi/2⌋)w_{i+1} = w_i + \max(32, \lfloor w_i/2 \rfloor), at most 12 attempts. For binary64 the sequence is 117,175,262,…,10053117, 175, 262, \ldots, 10053 bits. Ziv’s argument for termination is that f(x)f(x) is not a breakpoint of ∘\circ: by Lindemann–Weierstrass, exe^{x}, ln⁡x\ln x, sin⁡x\sin x, cos⁡x\cos x, tan⁡x\tan x and their inverses are transcendental at every nonzero algebraic (in particular dyadic) argument other than the trivial exceptions, while breakpoints are dyadic. The exceptions are filtered before the loop: e0=1e^0 = 1, ln⁡1=0\ln 1 = 0, log⁡22k=k\log_2 2^k = k, 2n2^n for integral nn, sin⁡(±0)\sin(\pm 0), integral and half-integral arguments of sinpi and cospi, tanpi⁡(±1/4)=±1\operatorname{tanpi}(\pm 1/4) = \pm 1, 10n10^n for integral nn, log⁡1010n=n\log_{10} 10^n = n, and so on. For the π\pi-scaled functions Niven’s theorem shows that these are the only dyadic results.88 I. Niven, Irrational Numbers, 1956, Corollary 3.12: if rr is rational and sin⁡(πr)\sin(\pi r) is rational, then sin⁡(πr)∈{0,±12,±1}\sin(\pi r) \in \{0, \pm\frac12, \pm 1\}; similarly for cos⁡\cos, and tan⁡(πr)∈{0,±1}\tan(\pi r) \in \{0, \pm 1\}. The value ±12\pm\frac12 needs rr with denominator 6 or 3, which is not dyadic. A non-breakpoint has a positive distance to every breakpoint, so a large enough ww certifies it. How large ww must be is the table maker’s dilemma: no useful a priori bound is known for arbitrary pp, so the budget is a resource limit, not a correctness condition. When it runs out the try_* form reports a CertificationFailure with the stage, the reason and the last ww, and the total forms return a quiet NaN with invalid_operation; neither returns an uncertified value. The pinned MPFR corpus never exhausts it. One family of exceptions is not filtered on the current branch: pow with a non-integral exponent other than 1/2k1/2^k whose result is nevertheless dyadic, such as 163/4=816^{3/4} = 8. Under nearest rounding the enclosure still certifies the right value but inexact is raised; under a directed rounding the loop cannot certify and returns a CertificationFailure.

Integer powers. pow_int_ctx computes xnx^n by an addition chain at w=p+bits⁡(n)+bits⁡(p)+4w = p + \operatorname{bits}(n) + \operatorname{bits}(p) + 4 bits with round-to-nearest. Each chain step ak=ai+aja_k = a_i + a_j multiplies two approximations; if xa∏(1+δ)E(a)x^{a} \prod (1 + \delta)^{E(a)} describes the error structure, then E(ak)=E(ai)+E(aj)+1E(a_k) = E(a_i) + E(a_j) + 1 and E(1)=0E(1) = 0, so by induction E(a)≤a−1E(a) \le a - 1. The computed value is therefore xn(1+θ)x^n (1 + \theta) with ∣θ∣≤γn−1=(n−1)uw/(1−(n−1)uw)|\theta| \le \gamma_{n-1} = (n-1)u_w/(1 - (n-1)u_w) in Higham’s notation, which is below 2bits⁡(n)2^{\operatorname{bits}(n)} units in the last place of the ww-bit result. The code uses the radius 2bits⁡(n)+22^{\operatorname{bits}(n) + 2} units (one more factor of two for a negative power, whose reciprocal adds nn further factors), builds the interval, and accepts when both ends round alike, by the same (R2) argument. When 12 doublings do not certify, the exact power cn2nec^n 2^{ne} is formed and rounded, so the result is correctly rounded in every case. A power certainly outside the range is decided first from certified log⁡2\log_2 bounds, and powers whose exact value fits in pp bits are computed exactly, which also guarantees that the Ziv path only sees inexact results.

The coefficient kernel

Problem. Precision costs integer multiplication and division of pp-bit numbers, and pp ranges from 1 to 2282^{28}.

Choice. BinCoeff stores up to 128 bits inline and larger values as little-endian 32-bit limbs (a host bigint on JavaScript), and dispatches on the shorter operand length nn in limbs:

ProductNativeLLVMWasm, Wasm-GC
schoolbook below969696
Karatsuba from969696
Toom-3 from204820484096
two-prime NTT multiply from204820484096
NTT square from7687683072
recursive square from512768768

Sparse operands (few nonzero limbs) use a sparse product, and operands with m>2nm > 2n limbs are cut into nn-limb blocks. Division uses a one-limb loop, Knuth’s algorithm D below 48 divisor limbs, Burnikel–Ziegler recursion from 48 and a Newton reciprocal from 1024; GCD switches from the binary (Stein) algorithm to Lehmer batches above four limbs. The thresholds are measured, per target, by the benchmark suite; they are policy, not semantics.

Why exactness is preserved. Schoolbook, Karatsuba and Toom-3 evaluate integer polynomial identities, for example

(a1B+a0)(b1B+b0)=a1b1B2+[(a1+a0)(b1+b0)−a1b1−a0b0]B+a0b0,(a_1 B + a_0)(b_1 B + b_0) = a_1 b_1 B^2 + \bigl[(a_1 + a_0)(b_1 + b_0) - a_1 b_1 - a_0 b_0\bigr] B + a_0 b_0,

and Toom-3 (evaluation at 0,1,−1,2,∞0, 1, -1, 2, \infty) interpolates with exact divisions by 2 and 3 of signed intermediates known to be multiples, so they are exact integer computations. The NTT is the only modular step. It splits each operand into 16-bit digits, so each coefficient of the digit convolution is at most

min⁡(na,nb) (216−1)2<223⋅232=255\min(n_a, n_b)\,(2^{16} - 1)^2 < 2^{23} \cdot 2^{32} = 2^{55}

for transform lengths up to 2232^{23}. It computes the convolution modulo the primes p1=998244353=119⋅223+1p_1 = 998244353 = 119 \cdot 2^{23} + 1 and p2=754974721=45⋅224+1p_2 = 754974721 = 45 \cdot 2^{24} + 1, both of which have 2232^{23}-th roots of unity, and recombines by the Chinese remainder theorem, which is unique in [0,p1p2)[0, p_1 p_2) with p1p2≈259.4>255p_1 p_2 \approx 2^{59.4} > 2^{55}. So the recombined coefficients are the exact integers. The length check precedes every transform; a longer product uses overlapping blocks of admissible length or falls back to Toom-3. Division paths return (q,r)(q, r) with n=qd+rn = qd + r and 0≤r<d0 \le r < d by construction; the Newton path corrects its approximate quotient with the remainder and aborts if more than two corrections would be needed, which would indicate a bug rather than a numerical event. Because all paths compute the same integers, the choice of algorithm cannot change any rounded result, flag or encoding.

Ordering NaN in compare

Problem. MoonBit’s Compare trait asks for a three-way comparison that sorting and ordered maps can rely on. IEEE comparison is a partial order: NaN is unordered with everything, itself included.

Options. (1) Abort on NaN, as earlier versions did; every sort of data that might contain a NaN then becomes a crash. (2) Use IEEE totalOrder, which is total but distinguishes −0<+0-0 < +0 and puts negative NaNs below −∞-\infty, so compare would disagree with numerical equality on zeros. (3) Keep the numerical order on numbers and put all NaNs in one class above it.

Choice. Option (3). Define the key κ(x)=(0,x)\kappa(x) = (0, x) for a number and κ(NaN)=(1,0)\kappa(\mathrm{NaN}) = (1, 0), ordered lexicographically; compare(x, y) is the comparison of κ(x)\kappa(x) and κ(y)\kappa(y), with −0-0 and +0+0 mapped to the same number. A comparison of keys in a totally ordered set is reflexive, transitive and total, so compare is a total preorder; it is not antisymmetric (−0-0 and +0+0, or two NaNs with different payloads, compare equal but are different values), which Compare does not require. The cost is that nan > 1 is true under <, so code that needs IEEE semantics must use compare_checked (error on NaN), compare_quiet / compare_signaling (four-valued, with flags) or total_order. Structural == stays the derived Eq, because it is the only equality that is a congruence for every method (precision and payload included).

Correctness / invariants

  • Canonical form. Every finite value produced by the API has cc odd or c=0,e=0c = 0, e = 0, and bits⁡(c)≤\operatorname{bits}(c) \le its precision; stored exponents never saturate (a saturated exponent is classified as overflow or underflow first).
  • Correct rounding. For every arithmetic operation, conversion and elementary function and every context, the returned finite value equals ∘(r)\circ(r) for the exact real result rr, with the range rules above. By (R1), ∘(r)=r\circ(r) = r and no flag is raised whenever r∈Fr \in F; round_ctx is idempotent.
  • Flags. inexact iff ∘(r)≠r\circ(r) \ne r; overflow implies inexact; underflow iff tiny (per the context rule) and inexact; division_by_zero only for an exact infinite result of finite operands; invalid_operation iff a quiet NaN was produced from non-NaN operands or a signaling NaN was consumed. combine is associative, commutative and idempotent.
  • Error model. Consequently, in the normal range, ∣∘(r)−r∣≤u∣r∣|\circ(r) - r| \le u|r| for nearest and <2u∣r∣< 2u|r| for directed rounding, with the absolute term η/2\eta/2 (respectively η\eta) below 2emin⁡2^{e_{\min}}, and subnormal sums and differences are exact.
  • Monotonicity. Each operation is monotone in each argument where the real function is, because it is ∘∘f\circ \circ f with ∘\circ monotone; in particular RD and RU results bracket the exact value, which ball_float and sqrt_bounds_for_precision rely on.
  • Exactness theorems. remainder, scaleb in the normal range, copy_sign, neg, abs, logb, to_integral_* and decoding are exact; fma(a, b, -RN(ab)) is exact without underflow.
  • Complexity. Addition is linear in the operand length (and independent of the exponent gap, by the far-operand rule); multiplication follows the kernel table, O(n2)O(n^2) to O(nlog⁡n)O(n \log n); division and square root cost a constant number of multiplications of the same size at large nn; remainder costs O(log⁡(qx−qy))O(\log(q_x - q_y)) modular multiplications; an elementary function evaluates its series at the working precision ww, and because ww grows geometrically the total cost of all attempts is within a constant factor of the last one.

The longer proofs (the far-operand addition rule, nextUp, the remainder reduction, the Ziv acceptance test and the NTT bound) are collected in the attachment.

Rounding and exactness proofs for bin_float

Alternatives rejected

  • Host Double for anything but from_double. Routing binary16, binary32 or binary128 through Double double-rounds, loses signaling NaNs on some targets and cannot represent binary128 at all. Interchange encoding is done on BinCoeff bit patterns instead.
  • A fixed number of guard bits. Three guard bits suffice for addition of two pp-bit operands, but not for division, square root, conversions from decimal or operands wider than the context. Deciding from exact integer data (round, sticky, remainder sign, midpoint comparison) works for all of them with one finalizer.
  • Rounding to pp bits, then to the subnormal grid. This double rounding produces wrong subnormal results; the finalizer shifts once to the coarser of the two positions.
  • Ziv with an estimated error bound. It requires a separate error analysis for every function and every reduction, and a mistake in it silently returns a wrong last bit. Outward-rounded enclosures make the bound a computed quantity, at the cost of evaluating every operation twice.
  • Global flags and rounding state. IEEE 754 describes flags as sticky global state. A returned BinaryFlags value composes with pure code, concurrent code and the Result style of @lf_arith, and combine recovers the sticky behaviour where wanted.
  • compare aborting on NaN, or compare as totalOrder. See Ordering NaN in compare.

Boundaries

bin_float deliberately does not:

  • provide interval or ball arithmetic: enclosures are internal to the certification loops; ball_float builds midpoint–radius arithmetic on top of BinFloat;
  • implement IEEE 754 alternate exception handling (traps, substitution) or sticky global flags: flags are returned values;
  • implement decimal floating-point (decimal, decimal_gda) or the non-binary IEEE operations on them;
  • promise any NaN payload for a newly generated NaN (it uses payload 0), or propagate payloads of more than one input;
  • guarantee completion of an elementary function within any time for every input: certification has a budget, and an exhausted budget is reported, not hidden;
  • expose limb layout, thresholds or transform parameters: they may change without notice as long as every result, flag and encoding stays the same;
  • claim conformance beyond the finite corpus recorded in conformance.

Footnotes

  1. IEEE Std 754-2019, IEEE Standard for Floating-Point Arithmetic: clause 3 (formats), 4.3 (rounding-direction attributes), 5 (operations), 6 (infinities, NaNs, signed zero), 7 (default exception handling). ↩

  2. N. J. Higham, Accuracy and Stability of Numerical Algorithms, 2nd ed., SIAM 2002, §2.2; D. Goldberg, “What every computer scientist should know about floating-point arithmetic”, ACM Computing Surveys 23(1), 1991. ↩

  3. J.-M. Muller et al., Handbook of Floating-Point Arithmetic, 2nd ed., Birkhäuser 2018, §2.1 and §4.3. ↩

  4. Muller et al., Handbook of Floating-Point Arithmetic, §5.3 and §7.6. ↩

  5. T. J. Dekker, “A floating-point technique for extending the available precision”, Numerische Mathematik 18, 1971; Muller et al., §4.4. ↩

  6. The digit count ⌈plog⁡102⌉+1\lceil p \log_{10} 2 \rceil + 1 suffices for round trip (Matula 1968; Goldberg 1991, Theorem 15); the extra digit is a margin for the bisection’s upper end. ↩

  7. A. Ziv, “Fast evaluation of elementary mathematical functions with correctly rounded last bit”, ACM TOMS 17(3), 1991; for interval evaluation see W. Tucker, Validated Numerics, Princeton 2011, and F. Johansson, “Arb: efficient arbitrary-precision midpoint-radius interval arithmetic”, IEEE Trans. Computers 66(8), 2017. ↩

  8. I. Niven, Irrational Numbers, 1956, Corollary 3.12: if rr is rational and sin⁡(πr)\sin(\pi r) is rational, then sin⁡(πr)∈{0,±12,±1}\sin(\pi r) \in \{0, \pm\frac12, \pm 1\}; similarly for cos⁡\cos, and tan⁡(πr)∈{0,±1}\tan(\pi r) \in \{0, \pm 1\}. The value ±12\pm\frac12 needs rr with denominator 6 or 3, which is not dyadic. ↩