Skip to content

Add C++26 saturation arithmetic polyfills (#39) - #255

Closed
thirtytwobits wants to merge 4 commits into
mainfrom
claude/issue-39-saturating-arithmetic
Closed

thirtytwobits wants to merge 4 commits into
mainfrom
claude/issue-39-saturating-arithmetic

Conversation

@thirtytwobits

@thirtytwobits thirtytwobits commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

Summary

First of the two PRs agreed in #39 (proposal, decisions). It adds the C++26 saturation arithmetic as a true polyfill back to C++14, plus the two smaller standard facilities the consumers need around it. The cetl::saturating<T> wrapper type follows in a separate PR.

  • cetl/pf26/numeric.hpp: cetl::pf26::saturating_add, saturating_sub, saturating_mul, saturating_div, saturating_cast, exact to [numeric.sat] under the final P4052R0 names: one operand type that is never promoted, bool and the character types excluded, y != 0 as a precondition on division (CETL_DEBUG_ASSERT, CDE_numeric_001), and min / -1 saturating to max. __builtin_*_overflow is used where the compiler has it (GCC 5+, Clang); otherwise a portable implementation that never forms an overflowing expression. Both are constant expressions in C++14 and both are compiled and tested on every toolchain.
  • cetl/pf26/cetlpf.hpp: selects the implementation by __cpp_lib_saturation_arithmetic, not __cplusplus (every current compiler still reports 202400L for -std=c++26): >= 202603L uses std::saturating_* directly; >= 202311L (libstdc++ 14/15, libc++ 18–22) forwards to std::add_sat/std::saturate_cast under the final names; otherwise cetl::pf26.
  • cetl/pf20/utility.hpp: cmp_equal, cmp_not_equal, cmp_less, cmp_greater, cmp_less_equal, cmp_greater_equal, in_range (C++20 polyfills; saturating_cast needs sign-correct comparisons). Wired into cetl/pf20/cetlpf.hpp.
  • The integer traits behind both headers ignore cv-qualifiers (so saturating_add<const bool> is rejected like bool) and support GCC's __int128 in the strict -std=c++NN modes where the standard traits and std::numeric_limits do not describe it.
  • cetl/pf17/algorithm.hpp: clamp (C++17 polyfill; nunavut's saturated intN fields clamp to an arbitrary range). Wired into cetl/pf17/cetlpf.hpp.
  • cetl/cetl.hpp: CETL_CPP_STANDARD_23 / CETL_CPP_STANDARD_26 and the example 13 page.
  • CETLVaSt: cpp-26 presets generated with tcpm for every toolchain except GCC 7, cpp-26 added to the amd64 CI matrix (in ts26.4.5 both g++ 15 and clang 22 take the 202311L branch, so CI exercises the adapted-std path; the polyfill path is covered by C++14/17/20/23).

Tests

  • test_pf26_numeric.cpp: constant-expression cases; every operand pair for int8_t/uint8_t; every first operand of int16_t/uint16_t against edge and sampled second operands; edge pairs and 100k random pairs for the 32-bit types against a long long oracle and for the 64-bit types against a 128-bit oracle (where available); saturating_cast from every value of the 8- and 16-bit types into every fixed-width type; 64-bit edge tables; the portable implementations checked alongside the builtin ones; the cetlpf.hpp selection; a death test for division by zero.
  • test_pf20_utility.cpp: the classic mixed-sign cases as constant expressions and a sweep over every pair of fixed-width types against a sign-aware oracle.
  • test_pf17_algorithm.cpp: value, reference-identity, comparator and floating-point cases, and a death test for inverted bounds (CDE_algorithm_001).
  • Compile-failure tests: bool operands and mixed operand types for saturating_add, bool for cmp_less.
  • fuzz_saturation_arithmetic.cpp: differential libFuzzer target against a 128-bit oracle for all five functions and every fixed-width type, with a seed corpus; the fuzz CMake now loops over its targets.
  • example_13_saturating_arithmetic.cpp: counter saturation, uptime narrowing, explicit operand type, clamp and in_range.

Validation

Toolchain / mode Path exercised Result
GCC 13, C++14, Debug + Release cetl::pf26 polyfill (builtins and portable) full run_unittests + run_examples pass; compile-failure tests 9/9
Homebrew clang 23 / libc++ 23, C++26, Debug + DebugEP + Release std::saturating_* (202603L) new tests pass; compile-failure tests 9/9
g++-15 / libstdc++ 15, C++26, Debug std::add_sat family adapted (202311L) new tests pass; compile-failure tests 9/9
g++-7.5 (toolshed ts24.4.3), C++14 syntax pass of every new test source with CETL's warning set clean
clang-format 19.1.7 (ts24.4.3) and 22.1.2 (ts26.4.3) all touched headers clean
Doxygen 1.14 generate_CETL_docs no new warnings; example 13 page and pf26/cetlpf.hpp snippets render
libFuzzer (Homebrew clang, ASan + UBSan, C++14) both fuzz smoke tests pass

Not covered locally: Apple clang in C++26 mode (CMake 4.1 has no CXX26 flags for AppleClang); the pre-rename libc++ branch was only exercised with a standalone build during the design phase.

Refs #39.

🤖 Generated with Claude Code

@thirtytwobits

Copy link
Copy Markdown
Member Author

@codex review

@thirtytwobits
thirtytwobits added this pull request to stack #257 October 22, 2026 17:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-22T18:20:38.619991Z e433f70 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a530f449e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread include/cetl/pf20/utility.hpp Outdated
Comment thread include/cetl/pf26/numeric.hpp Outdated
Comment thread include/cetl/pf26/cetlpf.hpp
thirtytwobits added a commit that referenced this pull request Oct 22, 2026
Addresses the Codex review of #255.

The shared integer traits in cetl/pf20/utility.hpp now ignore
cv-qualifiers, so explicit template arguments such as
saturating_add<const bool> or cmp_less<const char, char> are rejected
exactly like their unqualified forms, while const int is accepted on
every implementation path.

GCC's __int128 is now supported in the strict -std=c++NN modes CETLVaSt
builds with, where std::is_integral, std::is_signed, std::make_unsigned
and std::numeric_limits do not describe it: the traits carry their own
signedness, unsigned counterpart and two's-complement limits for the
128-bit types, and cetl/pf26/numeric.hpp uses those traits throughout
instead of the standard ones.

The forwarders used when the standard library only has the P0543R3
names get trailing return types that depend on the standard call, so
they participate in overload resolution exactly when the standard
function would.

Tests: cv-qualified explicit template arguments; 128-bit constant
expressions for the comparison functions, in_range and all five
saturation functions including the portable implementations; a
compile-failure test for const bool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@thirtytwobits

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: e433f700ac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@MAMONTBOT MAMONTBOT left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes; no blocking issues found.

MAMONTBOT
MAMONTBOT previously approved these changes Oct 22, 2026

@MAMONTBOT MAMONTBOT left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes; no blocking issues found.

thirtytwobits and others added 2 commits October 24, 2026 10:57
libcyphal and nunavut both need saturating integer operations, and C++26
standardized them (P0543R3, renamed by P4052R0 to saturating_add,
saturating_sub, saturating_mul, saturating_div and saturating_cast). CETL
now provides those five functions for C++14 and newer, exact to the
standard: one operand type that is never promoted, bool and the character
types excluded, a y != 0 precondition on division, and min / -1 saturating
to max. The compiler's overflow builtins are used where present and a
portable implementation that never forms an overflowing expression
otherwise; both are constant expressions in C++14 and both are tested.

cetl/pf26/cetlpf.hpp selects the implementation by the library feature-test
macro rather than __cplusplus, because current compilers still report an
interim value for -std=c++26 and because the functions were renamed late:
202603L uses the standard names directly, 202311L forwards to the earlier
std::add_sat family under the final names, and anything else uses
cetl::pf26.

saturating_cast needs sign-correct comparisons, so the C++20 integer
comparison functions (cmp_equal, cmp_less, ..., in_range) are added as
cetl::pf20 polyfills, and nunavut's "saturated intN" fields need a clamp to
an arbitrary range, so std::clamp is added as a cetl::pf17 polyfill. Both
are wired into their cetlpf.hpp headers.

Tests: exhaustive 8-bit and sampled 16-bit operand pairs, random 32/64-bit
operands against a 128-bit oracle, exhaustive saturating_cast from the 8-
and 16-bit types into every fixed-width type, an integer-comparison sweep
against a sign-aware oracle, debug-assert death tests, compile-failure tests
for bool and mixed operand types, and a differential libFuzzer target. A new
cpp-26 preset is generated for every toolchain except GCC 7 and added to the
amd64 CI matrix so the standard-library paths are exercised. Example 13 and
CETL_CPP_STANDARD_23/26 document the addition.

Refs #39; the cetl::saturating<T> wrapper type follows in a separate PR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Addresses the Codex review of #255.

The shared integer traits in cetl/pf20/utility.hpp now ignore
cv-qualifiers, so explicit template arguments such as
saturating_add<const bool> or cmp_less<const char, char> are rejected
exactly like their unqualified forms, while const int is accepted on
every implementation path.

GCC's __int128 is now supported in the strict -std=c++NN modes CETLVaSt
builds with, where std::is_integral, std::is_signed, std::make_unsigned
and std::numeric_limits do not describe it: the traits carry their own
signedness, unsigned counterpart and two's-complement limits for the
128-bit types, and cetl/pf26/numeric.hpp uses those traits throughout
instead of the standard ones.

The forwarders used when the standard library only has the P0543R3
names get trailing return types that depend on the standard call, so
they participate in overload resolution exactly when the standard
function would.

Tests: cv-qualified explicit template arguments; 128-bit constant
expressions for the comparison functions, in_range and all five
saturation functions including the portable implementations; a
compile-failure test for const bool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@thirtytwobits
thirtytwobits force-pushed the claude/issue-39-saturating-arithmetic branch from e433f70 to 4dc1c3f Compare October 24, 2026 17:57
thirtytwobits and others added 2 commits October 24, 2026 20:57
The release tooling's required_jobs() must list every CETLVaSt matrix
entry, and its contract test fails when the workflow matrix and the list
disagree. The cpp-26 standard added to the amd64 matrix was missing from
the list, which failed release-tests and therefore release-ready on #255.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thirtytwobits added a commit that referenced this pull request Oct 25, 2026
Addresses the Codex review of #255.

The shared integer traits in cetl/pf20/utility.hpp now ignore
cv-qualifiers, so explicit template arguments such as
saturating_add<const bool> or cmp_less<const char, char> are rejected
exactly like their unqualified forms, while const int is accepted on
every implementation path.

GCC's __int128 is now supported in the strict -std=c++NN modes CETLVaSt
builds with, where std::is_integral, std::is_signed, std::make_unsigned
and std::numeric_limits do not describe it: the traits carry their own
signedness, unsigned counterpart and two's-complement limits for the
128-bit types, and cetl/pf26/numeric.hpp uses those traits throughout
instead of the standard ones.

The forwarders used when the standard library only has the P0543R3
names get trailing return types that depend on the standard call, so
they participate in overload resolution exactly when the standard
function would.

Tests: cv-qualified explicit template arguments; 128-bit constant
expressions for the comparison functions, in_range and all five
saturation functions including the portable implementations; a
compile-failure test for const bool.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thirtytwobits added a commit that referenced this pull request Oct 25, 2026
The release tooling's required_jobs() must list every CETLVaSt matrix
entry, and its contract test fails when the workflow matrix and the list
disagree. The cpp-26 standard added to the amd64 matrix was missing from
the list, which failed release-tests and therefore release-ready on #255.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@thirtytwobits
thirtytwobits deleted the claude/issue-39-saturating-arithmetic branch October 25, 2026 15:36
thirtytwobits added a commit that referenced this pull request Oct 25, 2026
The release tooling's required_jobs() must list every CETLVaSt matrix
entry, and its contract test fails when the workflow matrix and the list
disagree. The cpp-26 standard added to the amd64 matrix was missing from
the list, which failed release-tests and therefore release-ready on #255.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

3 participants