FP32 per-op error bounds #
Proofs about numerical code should not have to expand the definition of “float32 rounding” every
time they use + or exp. This file provides reusable rewrite theorems for FP32
rounding-error reasoning.
We collect those lemmas for TorchLean’s FP32 model:
- each primitive op is interpreted as “compute in
ℝ, then round to the binary32 grid”, and - the rounding step has a standard half-ULP absolute error bound.
This mirrors the standard mental model behind PyTorch/IEEE float analysis (even though our proof backend is not a bit-level model): it’s the classic “real computation + rounding error” split.
The pattern is always:
- compute the exact real result,
- apply the
FP32rounding operator, - bound the rounding error by $\varepsilon_{32}(x)$, half an ulp at $x$; see
NN/Floats/FP32/Notation.lean.
Important nuance: these are local statements about a single rounding step. Whole-network bounds typically combine these with:
- algebra/triangle-inequality steps, or
- higher-level composition lemmas in
NN/Proofs/RuntimeApprox/*.
References #
- IEEE Std 754-2019, "IEEE Standard for Floating-Point Arithmetic".
- D. Goldberg, "What Every Computer Scientist Should Know About Floating-Point Arithmetic", ACM Computing Surveys, 1991.
- N. J. Higham, "Accuracy and Stability of Numerical Algorithms", SIAM, 2nd ed., 2002.
This module works with a proof-friendly float32 semantics ("real operation + one rounding step"). Relating this abstraction to specific hardware/libm behavior is a separate, target-specific trust boundary handled elsewhere in TorchLean.
Per-op rounding error lemmas #
Core rounding lemma for the binary32 parameters fixed by FP32.
This is the “one thing we use everywhere”: once you know an operation is defined as “round the real result”, the proof goal reduces to an instance of this lemma.
Informal: if fl32(x) denotes rounding x : ℝ to the binary32 grid, then
$|\operatorname{fl}_{32}(x)-x|\le\varepsilon_{32}(x)$.
Addition: FP32 adds in ℝ and then rounds once.
This lemma isolates the rounding error introduced by that last step.
Informally, $|\operatorname{fl}_{32}(a+b)-(a+b)|\le\varepsilon_{32}(a+b)$.
The exact residual left by FP32 addition is itself representable when both operands lie on the binary32 grid. This structural fact is used by error-free transformations and is stronger than the accompanying half-ULP inequality.
Division: one real division followed by one rounding step.
This does not say division is “well-conditioned”; it only isolates the rounding stage.
Informally, $\left|\operatorname{fl}_{32}(a/b)-a/b\right|\le\varepsilon_{32}(a/b)$.
Transcendentals (proof semantics) #
In FP32, transcendental functions are specified as: apply the real function, then round.
That is not how hardware/libm is implemented, but it is exactly the right abstraction for proofs: it gives a clear mathematical meaning and a one-rounding-step error theorem.
Informally, $|\operatorname{fl}_{32}(\exp x)-\exp x|\le\varepsilon_{32}(\exp x)$, and similarly for the other functions below.
log in the proof model: real log followed by rounding.
Domain note: the inequality is about rounding error around Real.log a.val; it does not assert
anything about a.val > 0.
Informally, $|\operatorname{fl}_{32}(\log x)-\log x|\le\varepsilon_{32}(\log x)$.
abs in the proof model: real absolute value followed by rounding.
Even though $|x|$ is exact over $\mathbb R$, we still round the result because this is a “round after every primitive” semantics.
Informally, $\left|\operatorname{fl}_{32}(|x|)-|x|\right|\le\varepsilon_{32}(|x|)$.