`round`/`floor`/`ceil` with an integer representation built a one-unit
delta via `representation_values<Rep>::one() * reference`. That operator
is `[[deprecated]]` for offset units (e.g. temperatures) because such a
product is ambiguous between a point and a delta, so calling these
functions on `deg_C`/`deg_F` quantities triggered a deprecation warning
(a hard error under `-Werror=deprecated-declarations`).
Replace the pattern with the explicit two-argument `quantity{value,
reference}` constructor (the delta interpretation, matching what `delta`
does internally) everywhere it appeared:
- `math.h`: `floor`, `ceil`, `round`
- `bits/sudo_cast.h`: the zero-delta used in quantity_point conversions,
where the reference is genuinely an offset unit for temperature points
- `utility/random.h`: all distributions, whose `Q` is always a delta
Add regression tests exercising `floor`/`ceil`/`round` on integer
`deg_C` deltas (including negatives and round-half-to-even) plus the
originally reported `round<deg_F>(delta<deg_C>(1))` case.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
safe_int, constrained, their error policies, and the safe_iN aliases are
non-framework add-ons, so their public names move out of mp_units into the
mp_units::utility extension tier (next to the representation concepts already
there). The headers move to core/include/mp-units/utility/ accordingly but stay
in the core component: safe_int reuses the core 128-bit integer toolkit
(integral, is_signed_v, int128_t, ...) that also backs the scaling engine, so it
bridges to mp_units::detail with a single using-directive rather than relocating
or duplicating that toolkit. overflow_policies stays framework (it backs bounded
quantity point origins). No deprecation shims: these types are unreleased.
Also fixes utility/representation.h, whose public concepts delegated via the
unqualified detail::X. That resolved to mp_units::detail only as long as
mp_units::utility::detail did not exist; now that constrained/safe_int introduce
it, the references are qualified to ::mp_units::detail:: so they stay
order-independent (this surfaced only in the module build).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The cartesian_vector and random distribution types now live in
mp_units::utility. Turn the transitional mp_units:: shims into proper
[[deprecated]] aliases (gcc-12 keeps a plain using-declaration because
CTAD through a deprecated alias template is broken there), and migrate
all in-tree consumers to mp_units::utility:: so the deprecations don't
trip -Werror. cartesian_tensor keeps no shim: it is unreleased (added in
2.6.0, never shipped), so it lives only at mp_units::utility.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`embed` is the canonical inclusion of the plane into space and `project` the
canonical projection back, for both vectors and tensors:
- vector: embed (x,y) -> (x,y,0); project (x,y,z) -> (x,y)
- tensor: embed the 2x2 into the top-left block of a zeroed 3x3; project keeps
that top-left 2x2 block
They are explicit named free functions (no implicit cross-dimension conversion),
each defined for a single source dimension (embed: 2D only; project: 3D only).
The zero fill is the additive identity derived from a component (`x - x`), so no
value-initialization of the element type is required.
Also route `tensor_product` through `cartesian_tensor_from` instead of a
default-constructed result, matching the other constructing operations (the
element type need not be default-constructible).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`cartesian_vector<T, N>` and `cartesian_tensor<T, N>` now take a compile-time
dimension N in {2, 3} (default 3), so a planar model pays nothing for an unused
third component. Operations close at a matching N: tensor_product of two
N-vectors yields an N x N tensor, inner_product(N x N tensor, N-vector) yields
an N-vector, and mixing dimensions is ill-formed. vector_product is the 3D axial
vector for N=3 and the 2D perp-dot (pseudo)scalar for N=2. N is deduced from the
argument count (2/3 components -> 2D/3D vector; 4/9 -> 2x2/3x3 tensor).
Also:
- element conversions build via member initializer lists (no default-construct-
then-assign), and every constructing tensor operation builds through a new
cartesian_tensor_from helper instead of a default-constructed result, so the
element type need not be default-constructible
- a compile-time `extent` query (per-axis count, std::extent semantics) on both,
usable as both `x.extent` and `x.extent()`
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The converting and variadic constructors (and converting assignment) keyed their implicit/explicit
decision on `std::convertible_to` alone, which let a truncating floating-point -> integer element
conversion (e.g. `cartesian_vector<double>` -> `cartesian_vector<int>`) convert implicitly -
inconsistent with a quantity's rep, where the conversion must also be non-truncating.
Add a `detail::ImplicitlyConvertibleScalar` guard that keeps `std::convertible_to` (an element type
need not be fundamental and may define its own implicit-conversion rules) and defers the
non-truncating decision to the `implicitly_scalable` customization point, passing identical `one`
units so it degenerates to the rep-only decision. Deferring to the CPO rather than hardcoding its
`treat_as_floating_point`-based default keeps element conversions consistent with a user's
`implicitly_scalable` specialization. An FP target or a widening stays implicit; an FP -> integer
element conversion becomes explicit (still constructible). Converting assignment is restricted to the
implicit case for the same reason.
Adds static_assert coverage to the cartesian vector/tensor runtime tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consolidate the representation opt-outs into one character-agnostic customization
point and align the documentation with the two-axis model.
- Add `disable_representation<T>` in `customization_points.h`: a specializable
opt-out that bars a type from being a quantity representation regardless of
character. Its default (`is_quantity_abstraction<value_type_t<T>>`) rejects a
quantity or quantity-like type, and any container of them; `bool` is opted out
explicitly. This retires both `disable_real` and the `NotQuantity` guard.
- Rename the internal predicate `is_quantity_like` -> `is_quantity_abstraction`
(it covers `Quantity || QuantityLike`) so it no longer collides with the
`QuantityLike` customization family.
- The representation tier now leads with one shared `RepresentationBaseline`
guard (not opted out + `UnitMagnitudeScalable`), which short-circuits a quantity
before any character concept instantiates its operators (avoiding a
satisfaction cycle).
- Docs: restructure `representation_types.md` around the two character axes
(field via `numeric_field`, order via `tensor_order`) with a common baseline,
the new `disable_representation` section, and consistent unit-magnitude-aware
scaling terminology; align `concepts.md` and `using_custom_representation_types.md`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the `disable_vector`/`disable_tensor` opt-outs with intrinsic,
adapter-overridable traits and split representation classification into two tiers.
- `customization_points.h` now owns the customizable surface: the `real`/`imag`/
`modulus` CPOs plus the `numeric_field` (field axis) and `tensor_order` (order
axis) traits. Adapters specialize these; the derived concepts stay with the
rest of the concept model in `representation_concepts.h`.
- `numeric_field` is the single source of truth for the field axis (default:
`real()`/`imag()` API detection). Field matching is exact and disjoint - a real
quantity needs a real representation and a complex one a complex representation.
- `tensor_order` is detected structurally (two-index access -> 2, one-index -> 1,
otherwise 0) and overridable. Order matching is rank-ordered: a lower-order
representation fills a higher-order slot.
- Character concepts (`Real`/`Complex`, rank-ordered `Scalar`/`Vector`/`Tensor`)
carry no representation-validity, so in V3 they can also classify a quantity by
its character. The `*Representation` concepts are `NotQuantity`-first + character
+ `UnitMagnitudeScalable`; leading with `NotQuantity` rejects a quantity before
its operators are instantiated, avoiding a constraint-satisfaction cycle.
- Eigen/Blaze adapters declare `numeric_field` from their element type (they expose
`real()`/`imag()` on real types too, which the API default would misread as complex).
- Rename `MagnitudeScalable` -> `UnitMagnitudeScalable` and `UsesMagnitudeAwareScaling`
-> `UsesUnitMagnitudeAwareScaling` to disambiguate scaling by a `unit_magnitude`
from the value/L2 magnitude (the `magnitude` CPO).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace `using enum quantity_character_legacy` plus bare character names in the ISQ/HEP
tests with local `quantity_character` constants (`scalar`, `complex_scalar`, `vector`,
`tensor`) built from `quantity_tensor_order` / `quantity_field`, and switch the
`RepresentationOf` / `QUANTITY_SPEC_` character arguments in the concept and quantity-spec
tests to the bare-axis spelling. `real_scalar` is renamed to `scalar` for consistency with
the order-named constants. `apparent_power` (derived from the complex `complex_power`)
pins `quantity_field::real`, matching the library definition.
This removes the final legacy-enum usage from the repository.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `cartesian_tensor<T>`, a fixed 3x3 second-order Cartesian tensor that
serves as a representation type for tensor-character quantities (e.g.
`isq::stress`, `isq::strain`, `isq::moment_of_inertia`), which previously had
no instantiable representation. It mirrors the `cartesian_vector` design
(hidden-friend operations in a `*_iface` base, const-qualified operator
constraints, no `operator%`).
Operations follow ISO 80000-2:2019 clause 18 (second order): `+`, `-`, unary
`-`, scalar `*`//`, compound assignments, `==`; `tensor_product` (dyadic of two
vectors, 2-18.21), `inner_product` (tensor.tensor 2-18.23, tensor.vector
2-18.24), `scalar_product` (double-dot `:`, 2-18.25), and a Frobenius norm.
Fourth-order `T(x)S` (2-18.22), `transpose`/`trace`, and `a.T` are intentionally
out of scope for now.
Activate the previously stubbed tensor representation concepts: `Tensor` is a
permissive mirror of `Vector` (a tensor of order zero is a scalar and of order
one is a vector, ISO 80000-2:2019 18), so real scalars and `cartesian_vector`
also satisfy tensor character as degenerate lower-rank cases. A genuine
second-order `cartesian_tensor` opts out of the lower-rank `Vector` concept via
`disable_vector`, and `std::complex` opts out of `Tensor` via `disable_tensor`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refine the requires-expressions of the `cartesian_vector` operators to
constrain on `const`-qualified operands, matching the fact that the
operators take their arguments by `const` reference. A type whose
`operator+` (etc.) is not const-correct would previously have wrongly
satisfied the constraint.
The compound-assignment constraints are ref-qualified as well (mutable
lhs, const rhs) while keeping the `-> std::same_as<T&>` return-type check.
Also strengthen the unit-vector test to assert the resulting component
values (not just that the norm is 1) and to exercise the free
`unit_vector()` function.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MSVC's Debug code generator does not dead-code-eliminate after an `if constexpr`
chain whose branches all return, so the fallthrough `return` after the chain is
flagged C4702 (unreachable) on instantiations where one of the branches is
selected. Release builds optimize the dead path out and never complain.
Restructure two places so the fallback lives inside an explicit `else` /
returning helper instead of a fallthrough:
* `enforce_bounds()` (quantity_point.h): wrap the `return q;` fallback in
`else { return q; }` after the existing `if constexpr` chain.
* `compare_quantities()` (quantity.h): hoist the floating-point / already-widest
fallback into a local `fallback_cmp` lambda invoked from both arms of the
`if constexpr` chain. Keeps the existing float-equal suppression.
For `CHECK_THROWS_AS` in constrained_test.cpp, Catch2's macro expands into a
try/catch whose catch arm becomes unreachable when the body calls a `[[noreturn]]`
function; suppress C4702 around that one TEST_CASE since the macro is not ours
to restructure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Fix#580: use fixed-point arithmetic for integer unit conversions
Introduce a fixed-point implementation for unit conversions involving
integer representations, avoiding loss of significant digits that
previously occurred when the conversion factor was not a whole number.
New files:
- src/core/include/mp-units/bits/fixed_point.h: double_width_int<T> and
fixed_point<T,n> types for exact rational scaling of integer values.
Uses __int128 when available (__SIZEOF_INT128__) for 64-bit integers.
- src/core/include/mp-units/framework/scaling.h: public scaling_traits<>
customization point and scale<To>(M, value) free function. Provides
built-in specializations for floating-point and integer-like types.
- test/static/fixed_point_test.cpp: static assertions for the new types.
- test/runtime/fixed_point_test.cpp: runtime arithmetic edge-case tests.
Modified:
- sudo_cast.h: replace hand-rolled conversion_value_traits / sudo_cast_value
machinery with a single scale<To::rep>(c_mag, ...) call.
- representation_concepts.h: add MagnitudeScalable concept; replace
ComplexScalar with HasComplexOperations (which is its definition).
- customization_points.h: add unspecified_rep tag and declare the primary
scaling_traits<> template.
- framework.h / CMakeLists.txt: wire in the new headers.
- hacks.h: add MP_UNITS_DIAGNOSTIC_IGNORE_PEDANTIC and
MP_UNITS_DIAGNOSTIC_IGNORE_SIGN_CONVERSION macros.
- example/measurement.cpp: add scaling_traits specializations for
measurement<T> to demonstrate the customization point.
- test/static/{international,usc}_test.cpp: disable two tests that are
blocked on issue #614.
Co-authored-by: Tobias Hanhart <burnpanck@users.noreply.github.com>
* Fix value_Type typo in floating_point_scaling_factor_type specialization
The partial specialization for types with a nested value_type used
'value_Type' (capital T) instead of 'value_type', making the entire
specialization dead code as the requires-clause could never be satisfied.
Also fix 'mantiassa' -> 'mantissa' in the adjacent comment.
* Fix docstring typos in scaling_traits documentation
- 'quantitiy' -> 'quantity'
- 'dictatet' -> 'dictated'
- 'convetrible' -> 'convertible'
- 'implemenation' -> 'implementation'
- 'availabe' -> 'available'
* Fix conflict resolution error: keep ComplexScalar name from master
When resolving the merge conflict in representation_concepts.h, the
PR's renamed version of the concept ('HasComplexOperations') was used
instead of master's established name ('ComplexScalar'). The two concepts
are semantically equivalent — burnpanck simply renamed it in his branch.
Revert to the canonical 'ComplexScalar' name while retaining the new
'MagnitudeScalable' concept which was the actual addition from the PR.
* Fix measurement.cpp: remove duplicate class definition from merge
The PR branched from a version where measurement<T> was defined inline
in measurement.cpp. Master later moved the class to example/include/
measurement.h and changed measurement.cpp to #include that header.
The squash merge therefore introduced a duplicate definition: the class
from the header and the PR's inline class were both visible, causing
an 'ambiguous reference' error. Remove the now-redundant inline class;
the scaling_traits specializations added by the PR work correctly with
the class from measurement.h.
* style: pre-commit
* docs: chapters anchors improved in the "custom representation" chapter
* docs: value conversions chapter improved
* refactor: scaling support refactored
* fix: clang-16 crash fixed
* docs: `measurement` example documentation updated to match changes
* fix: use exact wide-integer arithmetic for rational unit conversions on all platforms
On ARM / Apple Silicon, long double == double (64-bit mantissa). The old
fixed_point<T>(long double) initialiser lost ~12 bits of precision for 64-bit
integer types when representing the scaling ratio, producing an error of ~49
units for the 10/9 (degree → gradian) conversion with a 10^18 input value.
Fix by splitting the integer-path else-branch into two cases:
• Pure rational M (is_integral(M * (denominator(M) / numerator(M))) == true):
use (value * numerator) / denominator via double_width_int_for_t<> arithmetic.
This is exact on every platform regardless of long double width.
• Irrational M (involves π etc.): keep the long double fixed_point approximation.
These conversions are inherently approximate; small values still produce correct
truncated results on all platforms.
Update the test comment to reflect the new exact-arithmetic path.
Fixes CI failures on clang-18/ARM and apple-clang-16.
* fix: replace floating-point TeX-point test with exact integer equivalent
72.27 is not exactly representable as double (it rounds to 72.2699...96).
Multiplying by the conversion factor 100/7227 via long double gives a result
≥ 1.0 on x86 (80-bit long double, 64-bit mantissa) only by chance, but
0.99999...978 on ARM / Apple Silicon where long double == double (52-bit).
The correct mathematical statement is: 7227 tex_point = 100 inch (exact
rational relationship). Use that integer form instead of the inexact 72.27
double literal so the test is correct and platform-independent.
---------
Co-authored-by: Tobias Hanhart <burnpanck@users.noreply.github.com>
Replace runtime unit access (q.unit) with compile-time type extraction
(get_unit(R)) in the inverse function implementation. This prevents the
function from becoming an immediate function when consteval unit operators
are used, allowing inverse to work with runtime variables.
Key changes:
- math.h: Use get_unit(R) instead of q.unit in both code paths
- test: Add comprehensive runtime inverse tests
This surgical fix preserves maximum compile-time optimization while
enabling DSP applications that need runtime inverse calculations.
Fixes compilation error with Clang:
"call to immediate function 'inverse' is not a constant expression"
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>