Skip to content

Rational has no checked-arithmetic mode; intermediate cross-terms can overflow before final results do #130

Description

@Yaraslaut

Summary

morph::math::Rational has no checked-arithmetic mode. operator+/operator- on plain Rational pairs are fixed-width std::int64_t arithmetic, not saturating and not exception-throwing by signature — at ledger-realistic magnitudes, summing enough rows genuinely triggers real signed-integer overflow (undefined behavior), with no way for a calling application to detect this before committing corrupted state.

Evidence

A binary-search fuzz test (tests/test_ledger_rational_fuzz.cpp, added while implementing rung 5 / ledger) measures the exact boundary empirically, via real Rational::operator+ calls (not a hand-computed estimate): summing dp=2 legs at 10^9 minor units each, the boundary is exactly 9,223,372,037 rows (INT64_MAX / 10^9 + 1).

This isn't a hypothetical: while adding this very fuzz test, an early implementation attempt hit real signed-overflow UB directly (an exponentiation-by-squaring helper doubling one step past what it needed), independently confirmed by two rounds of scoped review before landing a provably-safe version. Rational's own arithmetic operators offer no built-in guard against the same class of bug in application code that doesn't happen to structure its summation as carefully.

Suggested direction

A checked-arithmetic mode — an expected<Rational, Overflow>-returning operator+/operator- alongside the existing noexcept ones, or at minimum a debug-mode overflow assertion — would let a ledger-scale (or otherwise large-magnitude) application detect this before committing corrupted state, rather than relying on the app never summing enough rows to hit the boundary in practice.

Reference

Filed from LASTRADA-Software/morph, branch ladder-ledger-rung5 (not yet merged), as docs/findings/001-rational-checked-arithmetic-mode.md (see that file for the same summary in the ladder's own findings-pipeline format, examples/FINDINGS.md) and tests/test_ledger_rational_fuzz.cpp for the reproducing test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: utilSubsystem: utilenhancementNew feature or requesttriage: validWell-framed; implement as written

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions