From 5cfd8e02f355b87775f4672a74fd2299cc3d2479 Mon Sep 17 00:00:00 2001 From: Peter Neiss Date: Mon, 22 Jun 2026 21:08:01 +0200 Subject: [PATCH] math: rename the double-backed storage flag real -> f64 (real kept as alias) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce `f64` as the canonical name for the binary64-backed storage flag and keep `real` as a bit-identical deprecated alias for one release. This frees the `real` name and sets up the sibling `f32` (binary32 storage) coming next; it also reflects that the flag is purely a STORAGE choice (transcendentals gate on snap). - policy_flag.hpp: `f64` is the flag (formerly `real`'s bits, unchanged); `real` is now `inline static constexpr policy_flag real = f64;`. Because the value is identical, every existing `has_flag(P, real)` / `| real` site — 27 test/example files and all library internals — compiles unchanged. - Tests: f64 selects the same double-backed raw as real and the two are the same flag (test_storage_flags.cpp). - Docs: policies.md + math.md present `f64` as canonical, `real` as deprecated. Behavior-identical; no internal `real_raw`/storage renames yet — those land with the f32 storage backend (next PR), where the f64/f32 pairing motivates them. Verified: default 405/405, CORDIC 442/442, FLOAT 396/396. Co-Authored-By: Claude Opus 4.8 --- docs/math.md | 11 ++++++++--- docs/policies.md | 8 ++++---- include/bound/policy_flag.hpp | 14 ++++++++++---- single_include/bound/bound.hpp | 14 ++++++++++---- tests/test_storage_flags.cpp | 18 ++++++++++++++++++ 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/docs/math.md b/docs/math.md index 12aebfa..f647d56 100644 --- a/docs/math.md +++ b/docs/math.md @@ -44,18 +44,23 @@ auto s = math::sin(angle{1}); // amplitude bound in [-1, 1] auto h = math::hypot(s, s); // √(s²+s²), output grid auto-deduced ``` -## The `snap` requirement (and `real` as a fast storage option) +## The `snap` requirement (and `f64` as a fast storage option) + +> **Naming:** the double-backed storage flag is now spelled **`f64`**; **`real` +> is a deprecated alias** kept for one release. The two are bit-identical, so the +> examples below (and existing code) compile under either spelling. New code +> should prefer `f64`. A transcendental result is irrational and must be **rounded onto the operand's grid**, so every transcendental operand must carry a policy that **permits -rounding** — i.e. the **`snap`** bit (`snap`, any `round_*` mode, or `real`, +rounding** — i.e. the **`snap`** bit (`snap`, any `round_*` mode, or `f64`, which implies `round_nearest`). Omitting it is a compile error. This is the only hard requirement: `math::sin` etc. work on **any snap-capable grid**, including plain integer grids and non-dyadic ones (e.g. a `notch<1,100>` money grid) — the value is computed by the engine and snapped to the grid via exact rational rounding. -`real` is **not** required — it is an optional **storage** flag that buys speed: +`f64` is **not** required — it is an optional **storage** flag that buys speed: - Under the default engine `real` selects **double-backed storage** on the bound's grid — the raw *is* the value, so input marshalling into the engine is diff --git a/docs/policies.md b/docs/policies.md index 6b9a20b..405ec95 100644 --- a/docs/policies.md +++ b/docs/policies.md @@ -49,7 +49,7 @@ is proven elsewhere. `clamp`, `wrap`, and `sentinel` are mutually exclusive | `round_half_even` | banker's rounding — half to even (implies `snap`) | | `ignore_zero` | skip the divide-by-zero check — `a / 0` / `a % 0` is UB (binary `div`/`mod`); compound `/= 0` / `%= 0` no-op | | `ignore_domain` | suppress the runtime domain check | -| `real` / `exact` / `direct` / `indexed` | **representation flags** — select how the raw value is stored; see the next section | +| `f64` / `exact` / `direct` / `indexed` | **representation flags** — select how the raw value is stored; see the next section (`real` is a deprecated alias of `f64`) | ## Representation flags @@ -59,7 +59,7 @@ Besides the *behavior* flags above, four flags select the **representation** | Flag | Forces | Grid requirement | Notes | |---|---|---|---| -| `real` | IEEE-754 `double` raw (the value itself, snapped to the grid) | dyadic **and** double-exact (every value fits `double`'s 53-bit significand) | bundles `round_nearest`; the math-operand flag — see [math.md](math.md#the-real-policy-requirement). Arithmetic drops `real` to an exact representation when a result grid is too fine for `double`. Under `BND_MATH_FIXED` it falls back to integer storage. | +| `f64` | IEEE-754 `double` raw (the value itself, snapped to the grid) | dyadic **and** double-exact (every value fits `double`'s 53-bit significand) | bundles `round_nearest`; the fast math-storage flag. Arithmetic drops `f64` to an exact representation when a result grid is too fine for `double`. Under `BND_MATH_FIXED` it falls back to integer storage. **`real` is a deprecated alias of `f64`.** | | `exact` | exact-fraction raw on **any** grid | none | no notch-count limit, no `double` anywhere; arithmetic is exact — on notched grids overflow is usually provably impossible and `+ − ×` return plain bounds (no `optional`) | | `direct` | raw == value as a plain integer | `Notch == 1` | e.g. `bound<{5, 100}, direct>` stores 5..100, not index 0..95 — the raw equals the wire/debugger value | | `indexed` | raw == 0-based notch index | `Notch != 0` | e.g. `bound<{-5, 5}, indexed>` stores 0..10 unsigned — dense layout for serialization | @@ -73,8 +73,8 @@ using slot = bound<{-5, 5}, indexed>; // raw() == 0..10, dense unsigned Binary arithmetic ORs the policies of both operands, so a result can carry several representation flags; storage selection resolves them -**widest-wins**: `exact > real > direct > indexed > deduced`. An -`exact + real` sum is therefore exact, and a `real` math chain stays +**widest-wins**: `exact > f64 > direct > indexed > deduced`. An +`exact + f64` sum is therefore exact, and a `f64` math chain stays double-backed end to end — no errors at mixed call sites. > **API-boundary shorthand:** the modern idiom for "saturate-and-round into diff --git a/include/bound/policy_flag.hpp b/include/bound/policy_flag.hpp index 85fa85f..0ae1301 100644 --- a/include/bound/policy_flag.hpp +++ b/include/bound/policy_flag.hpp @@ -45,13 +45,19 @@ namespace bnd // Representation flags — select raw storage. Without one, storage is deduced // from the grid (notch-0 → rational; unit notch at/below 0 → integer value; // else 0-based index). Binary ops OR operand policies; storage resolves - // widest-wins: exact > real > direct > indexed > deduced. + // widest-wins: exact > f64 > direct > indexed > deduced. // - // `real` — math operand. Double-backed storage under the default engine (value + // `f64` — math operand, binary64-backed storage under the default engine (value // held as IEEE-754 double, notch nominal); an ordinary round_nearest integer // bound under BND_MATH_FIXED. Power-of-2 notch + dyadic Lower required so - // on-grid values are exact in double. - inline static constexpr policy_flag real{(1ull << 37) | round_nearest}; + // on-grid values are exact in double (see `double_exact`). + inline static constexpr policy_flag f64{(1ull << 37) | round_nearest}; + + // `real` — deprecated spelling of `f64`, kept as an alias for one release. New + // code should use `f64` (binary64 storage); a future `f32` brings binary32 + // storage. The flag is purely a storage choice — transcendentals gate on + // `snap`, not on this (see cmath.hpp). + inline static constexpr policy_flag real = f64; // `exact` — force rational raw storage on any grid. Values still obey the grid; // exact fractions, no notch-count limit, no double. Slowest; overflow-checked diff --git a/single_include/bound/bound.hpp b/single_include/bound/bound.hpp index dc95468..8791d5b 100644 --- a/single_include/bound/bound.hpp +++ b/single_include/bound/bound.hpp @@ -3405,13 +3405,19 @@ namespace bnd // Representation flags — select raw storage. Without one, storage is deduced // from the grid (notch-0 → rational; unit notch at/below 0 → integer value; // else 0-based index). Binary ops OR operand policies; storage resolves - // widest-wins: exact > real > direct > indexed > deduced. + // widest-wins: exact > f64 > direct > indexed > deduced. // - // `real` — math operand. Double-backed storage under the default engine (value + // `f64` — math operand, binary64-backed storage under the default engine (value // held as IEEE-754 double, notch nominal); an ordinary round_nearest integer // bound under BND_MATH_FIXED. Power-of-2 notch + dyadic Lower required so - // on-grid values are exact in double. - inline static constexpr policy_flag real{(1ull << 37) | round_nearest}; + // on-grid values are exact in double (see `double_exact`). + inline static constexpr policy_flag f64{(1ull << 37) | round_nearest}; + + // `real` — deprecated spelling of `f64`, kept as an alias for one release. New + // code should use `f64` (binary64 storage); a future `f32` brings binary32 + // storage. The flag is purely a storage choice — transcendentals gate on + // `snap`, not on this (see cmath.hpp). + inline static constexpr policy_flag real = f64; // `exact` — force rational raw storage on any grid. Values still obey the grid; // exact fractions, no notch-count limit, no double. Slowest; overflow-checked diff --git a/tests/test_storage_flags.cpp b/tests/test_storage_flags.cpp index 9e00e9d..b3bfc5f 100644 --- a/tests/test_storage_flags.cpp +++ b/tests/test_storage_flags.cpp @@ -135,6 +135,24 @@ TEST_CASE("representation flags resolve widest-wins", "[storage][policy]") REQUIRE(DI{42}.raw() == 42); } +TEST_CASE("f64 is the canonical double-backed flag; real is its alias", + "[storage][f64]") +{ + // `real` was renamed `f64`; the alias is bit-identical, so old code compiles. + STATIC_REQUIRE(bnd::f64 == bnd::real); + STATIC_REQUIRE(has_flag(bnd::f64, round_nearest)); // still carries snap/round + +#ifndef BND_MATH_FIXED + // f64 selects binary64-backed storage exactly as `real` did (storage is + // independent of the compute engine — true in the double AND float builds). + using F = bound<{{0, 4}, notch<1, 256>}, round_nearest | f64>; + using R = bound<{{0, 4}, notch<1, 256>}, round_nearest | real>; + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(detail::real_raw); +#endif +} + TEST_CASE("representation flags compose with behavior policies", "[storage][policy]") {