Numerical accuracy in research code
Floating-point numbers are approximations with well-defined rules,
and research conclusions can hinge on respecting them. The two
recurring failures: treating floats as exact (== comparisons,
accumulating error blindly) and treating all differences as noise
(tolerances loosened until tests pass). Both are avoidable with a
small set of habits - and both matter more in research than
elsewhere, because the numbers ARE the result.
The ground rules
- Never compare floats with == (except against an exactly
representable sentinel like 0.0 you assigned yourself). Use
tolerance-based comparison:
math.isclose, numpy.isclose, or
the testing helpers below.
- Decimal literals are usually not representable: 0.1 is stored as
the nearest binary fraction, which is why 0.1 + 0.2 != 0.3.
Format-rounding for display hides this; arithmetic does not.
- NaN propagates and never equals anything, including itself; test
with isnan, and decide explicitly whether NaN in data means
missing, invalid or bug (rseng-data-management's missing-data
discipline). Silent NaN propagation into published numbers is the
classic silent failure.
- Precision-degrading operations (subtracting nearly equal numbers -
catastrophic cancellation; summing numbers of very different
magnitude) lose precision structurally; restructure the formula
(e.g. use expm1/log1p, two-pass variance algorithms) rather than
adding digits.
Tolerances: choose, do not tune
- Relative tolerance compares magnitudes ("within 1e-9 of each
other, proportionally") - right for values far from zero.
Absolute tolerance is required near zero, where relative
comparison degenerates. Robust comparisons combine both
(numpy.testing.assert_allclose(rtol=, atol=)).
- Derive tolerances from the problem: input data precision,
algorithm order, condition number of the operation - and record
the justification in a comment or the test name. A tolerance
someone loosened until CI passed documents nothing and hides
regressions (rseng-testing).
- Scientific sign-off: when a characterization or migration test
needs a tolerance decision (rseng-legacy-code), make it with the
domain expert - it is a statement about the science, not the code.
Precision and reproducibility across platforms
- float64 is the scientific default; float32 halves memory and can
double throughput (and is common on GPUs - rseng-gpu-computing) but
carries ~7 decimal digits: justify it per-array, not globally.
Mixed precision is an optimization to apply deliberately
(rseng-performance-profiling).
- Bit-identical results across machines are NOT promised by
IEEE-conformant code: compiler flags, SIMD width, BLAS
implementation, thread count and reduction order all legitimately
change last bits. Cross-platform tests therefore assert within
tolerances, never bitwise (a policy mature projects like the
astronomy stack encode in their contribution rules).
- What IS controllable: pin library versions
(rseng-reproducible-environments), fix seeds for stochastic parts,
avoid fast-math-style flags for result-bearing code, and document
the platform in published results (rseng-publishing-releasing).
- Parallel reductions reorder sums; if run-to-run variation appears
under threading, that is why - use deterministic-reduction options
where offered, or widen tolerances knowingly.
Accumulation and safe patterns
- Long naive sums accumulate error linearly; library sums (numpy)
use pairwise summation - prefer them over hand loops. Kahan
compensated summation is available when a manual loop is
unavoidable and precision matters.
- Prefer numerically stable library routines (lstsq over normal
equations, logsumexp over exp-sum-log) - the stable formulation is
usually one function call away.
- Validate against known solutions: analytic cases, conservation
laws and invariants make the best numerical tests because their
expected error is reasoned, not guessed.
Working with this skill
This skill is source-independent: it encodes IEEE-754 floating-point
practice as applied in scientific computing.
Learn more (verified):
Related skills
Check whether any of these applies before moving on:
- rseng-debugging - diagnosing cross-platform result differences
- rseng-gpu-computing - float32 and mixed precision trade-offs
- rseng-legacy-code - characterization-test tolerance sign-off
- rseng-performance-profiling - precision changes as deliberate optimization
- rseng-reproducible-environments - pinned libraries limit result drift
- rseng-testing - tolerance-based numerical test design
1---2name: rseng-numerical-accuracy3description: Covers floating-point correctness in research code: why 0.1 + 0.2 != 0.3, choosing absolute vs relative tolerances in tests, accumulation error and safe summation, precision choices (float32 vs float64), catastrophic cancellation, NaN and infinity handling, and cross-platform or cross-library result drift. Use PROACTIVELY when floating-point comparisons fail mysteriously, when writing numerical tests or choosing tolerances, when results differ across machines, compilers, BLAS builds or library versions, or when precision or numerical stability questions arise in analysis or simulation code.4license: CC-BY-4.05---67# Numerical accuracy in research code89Floating-point numbers are approximations with well-defined rules,10and research conclusions can hinge on respecting them. The two11recurring failures: treating floats as exact (== comparisons,12accumulating error blindly) and treating all differences as noise13(tolerances loosened until tests pass). Both are avoidable with a14small set of habits - and both matter more in research than15elsewhere, because the numbers ARE the result.1617## The ground rules1819- Never compare floats with == (except against an exactly20 representable sentinel like 0.0 you assigned yourself). Use21 tolerance-based comparison: `math.isclose`, `numpy.isclose`, or22 the testing helpers below.23- Decimal literals are usually not representable: 0.1 is stored as24 the nearest binary fraction, which is why 0.1 + 0.2 != 0.3.25 Format-rounding for display hides this; arithmetic does not.26- NaN propagates and never equals anything, including itself; test27 with isnan, and decide explicitly whether NaN in data means28 missing, invalid or bug (rseng-data-management's missing-data29 discipline). Silent NaN propagation into published numbers is the30 classic silent failure.31- Precision-degrading operations (subtracting nearly equal numbers -32 catastrophic cancellation; summing numbers of very different33 magnitude) lose precision structurally; restructure the formula34 (e.g. use expm1/log1p, two-pass variance algorithms) rather than35 adding digits.3637## Tolerances: choose, do not tune3839- Relative tolerance compares magnitudes ("within 1e-9 of each40 other, proportionally") - right for values far from zero.41 Absolute tolerance is required near zero, where relative42 comparison degenerates. Robust comparisons combine both43 (numpy.testing.assert_allclose(rtol=, atol=)).44- Derive tolerances from the problem: input data precision,45 algorithm order, condition number of the operation - and record46 the justification in a comment or the test name. A tolerance47 someone loosened until CI passed documents nothing and hides48 regressions (rseng-testing).49- Scientific sign-off: when a characterization or migration test50 needs a tolerance decision (rseng-legacy-code), make it with the51 domain expert - it is a statement about the science, not the code.5253## Precision and reproducibility across platforms5455- float64 is the scientific default; float32 halves memory and can56 double throughput (and is common on GPUs - rseng-gpu-computing) but57 carries ~7 decimal digits: justify it per-array, not globally.58 Mixed precision is an optimization to apply deliberately59 (rseng-performance-profiling).60- Bit-identical results across machines are NOT promised by61 IEEE-conformant code: compiler flags, SIMD width, BLAS62 implementation, thread count and reduction order all legitimately63 change last bits. Cross-platform tests therefore assert within64 tolerances, never bitwise (a policy mature projects like the65 astronomy stack encode in their contribution rules).66- What IS controllable: pin library versions67 (rseng-reproducible-environments), fix seeds for stochastic parts,68 avoid fast-math-style flags for result-bearing code, and document69 the platform in published results (rseng-publishing-releasing).70- Parallel reductions reorder sums; if run-to-run variation appears71 under threading, that is why - use deterministic-reduction options72 where offered, or widen tolerances knowingly.7374## Accumulation and safe patterns7576- Long naive sums accumulate error linearly; library sums (numpy)77 use pairwise summation - prefer them over hand loops. Kahan78 compensated summation is available when a manual loop is79 unavoidable and precision matters.80- Prefer numerically stable library routines (lstsq over normal81 equations, logsumexp over exp-sum-log) - the stable formulation is82 usually one function call away.83- Validate against known solutions: analytic cases, conservation84 laws and invariants make the best numerical tests because their85 expected error is reasoned, not guessed.8687## Working with this skill8889This skill is source-independent: it encodes IEEE-754 floating-point90practice as applied in scientific computing.9192Learn more (verified):93 - https://floating-point-gui.de - the floating-point guide94 - https://numpy.org/doc/stable/reference/routines.testing.html -95 NumPy testing helpers (assert_allclose and friends)96 - https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html -97 Goldberg, What Every Computer Scientist Should Know About98 Floating-Point Arithmetic99100<!-- related-skills:begin -->101102## Related skills103104Check whether any of these applies before moving on:105106- rseng-debugging - diagnosing cross-platform result differences107- rseng-gpu-computing - float32 and mixed precision trade-offs108- rseng-legacy-code - characterization-test tolerance sign-off109- rseng-performance-profiling - precision changes as deliberate optimization110- rseng-reproducible-environments - pinned libraries limit result drift111- rseng-testing - tolerance-based numerical test design112113<!-- related-skills:end -->