Skip to content

docs: Rational's operators saturate and log; four passages still described UB - #266

Merged
Yaraslaut merged 1 commit into
masterfrom
docs/221-rational-saturation
Aug 24, 2026
Merged

docs: Rational's operators saturate and log; four passages still described UB#266
Yaraslaut merged 1 commit into
masterfrom
docs/221-rational-saturation

Conversation

@Yaraslaut

Copy link
Copy Markdown
Member

Closes #221.

The defect

rational.hpp's +, - and * detect an overflow before forming the
result
, saturate toward the correctly-signed ±INT64_MAX/1, and log at error
naming checkedAdd/checkedSub/checkedMul. Four passages across three
documents still described the pre-saturation behaviour as undefined behaviour.

rational.md made both claims at once — its "Overflow & value-range envelope"
section and its Limitations bullet described silent UB, while line 200, a few
dozen lines below the first, says in bold:

The operators saturate; they never overflow.

A reader deciding whether they need the checked variants got the wrong mental
model twice in the same file.

What changed

Corrected in README.md, docs/spec/util/quantity_type.md, and rational.md
(operator table row, envelope section, the "garbage Rational / UBSan trap"
paragraph, and the Limitations bullet).

The correction is not "it saturates, all is well." A saturated result is
still wrong — clamped and inexact, and nothing in the return type distinguishes
it from an exact one. What changed is the failure mode: bounded and logged
rather than undefined and silent, with the checked variants available for a
caller that must branch on it. I wrote it that way rather than swapping one
over-simplification for another — the old text at least conveyed "don't rely on
this", and a naive fix would have lost that.

Verified against the code, not the issue

  • operator*=mulWouldOverflow()reportOverflow() + saturateToward()
  • operator+=addWouldOverflow() → same
  • reportOverflow() logs at error, naming the checked alternatives

Where the drift shows

tests/test_rational_checked.cpp:154 opens its saturation cases with:

"The saturating operators. Before these, every case below was undefined [behaviour]"

The behaviour was changed and tested; the specs were simply never updated to
match. That's the whole issue in one line.

check result
[saturate] tests 27 assertions in 7 cases
full suite 20657 assertions in 1170 cases
scripts/check_spec_citations.sh pass

Documentation only — no code changes.

…ribed UB

rational.hpp's +, - and * detect an overflow before forming the result,
saturate toward the correctly-signed ±INT64_MAX/1, and log at error naming
checkedAdd/checkedSub/checkedMul. Four passages across three documents still
described the pre-saturation behaviour as undefined behaviour, and rational.md
made both claims at once: its "Overflow & value-range envelope" section and its
Limitations bullet said silent UB, while line 200 -- a few dozen lines below the
first -- says in bold "The operators saturate; they never overflow."

Corrected in README.md, docs/spec/util/quantity_type.md, and rational.md (the
operator table row, the envelope section, the "garbage Rational / UBSan trap"
paragraph, and the Limitations bullet).

The correction is not simply "it saturates, all is well". A saturated result is
still wrong -- it is clamped and inexact, and nothing in the return type
distinguishes it from an exact one. What changed is the failure mode: bounded
and logged rather than undefined and silent, with checkedAdd/checkedSub/
checkedMul available for a caller that must branch on it. The text says that
rather than replacing one over-simplification with another.

The transition is recorded in the tests, which is where the drift shows:
tests/test_rational_checked.cpp:154 opens the saturation cases with "Before
these, every case below was undefined behaviour". The behaviour was changed and
tested; the specs were never updated to match.

Verified against the code rather than the issue: operator*= calls
mulWouldOverflow() then reportOverflow() + saturateToward(), operator+= the same
via addWouldOverflow(), and reportOverflow() logs at error. [saturate] tests
pass; full suite 20657 assertions / 1170 cases; spec-citation lint clean.

Closes #221
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Yaraslaut
Yaraslaut merged commit 1ce0c97 into master Aug 24, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Three-plus docs describe pre-saturation UB that no longer exists; rational.md contradicts itself

1 participant