diff --git a/example/custom_systems.cpp b/example/custom_systems.cpp index 8139420f..b2c3419d 100644 --- a/example/custom_systems.cpp +++ b/example/custom_systems.cpp @@ -33,7 +33,7 @@ using namespace units; namespace fps { struct foot : named_unit {}; -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -54,8 +54,8 @@ using length = quantity; namespace fps { -struct foot : named_scaled_unit {}; -struct yard : named_scaled_unit {}; +struct foot : named_scaled_unit(), metre> {}; +struct yard : named_scaled_unit(), foot> {}; struct dim_length : base_dimension<"L", foot> {}; @@ -103,7 +103,7 @@ template std::ostream& operator<<(std::ostream& os, const U& u) { using unit_type = std::remove_cvref_t; - return os << unit_type::ratio << " x " << unit_type::reference::symbol.standard(); + return os << as_ratio(unit_type::mag) << " x " << unit_type::reference::symbol.standard(); } void what_is_your_ratio() diff --git a/src/core/include/units/bits/base_units_ratio.h b/src/core/include/units/bits/base_units_ratio.h index c29658c2..2d9703e3 100644 --- a/src/core/include/units/bits/base_units_ratio.h +++ b/src/core/include/units/bits/base_units_ratio.h @@ -24,29 +24,25 @@ #include #include +#include #include namespace units::detail { -template - requires(E::den == 1 || E::den == 2) // TODO provide support for any den -constexpr ratio exp_ratio() -{ - const ratio base_ratio = E::dimension::base_unit::ratio; - const ratio positive_ratio = - E::num * E::den < 0 ? ratio(base_ratio.den, base_ratio.num, -base_ratio.exp) : base_ratio; - const std::intmax_t N = E::num * E::den < 0 ? -E::num : E::num; - const ratio ratio_pow = pow(positive_ratio); - return E::den == 2 ? sqrt(ratio_pow) : ratio_pow; -} - /** - * @brief Calculates the common ratio of all the references of base units in the derived dimension + * @brief Calculates the "absolute" magnitude of the derived dimension defined by this list. + * + * "Absolute" magnitudes are not physically observable: only ratios of magnitudes are. For example: if we multiplied + * all magnitudes in the system by the same constant, no meaningful results would change. However, in practice, we need + * to make some global choice for the "absolute" values of magnitudes, so that we can compute their ratios. + * + * The point of this function is to compute the absolute magnitude of a derived dimension, in terms of the absolute + * magnitudes of its constituent dimensions. */ template -constexpr ratio base_units_ratio(exponent_list) +constexpr Magnitude auto absolute_magnitude(exponent_list) { - return (exp_ratio() * ... * ratio(1)); + return (pow(Es::dimension::base_unit::mag) * ... * magnitude<>{}); } } // namespace units::detail diff --git a/src/core/include/units/bits/basic_concepts.h b/src/core/include/units/bits/basic_concepts.h index d3daf798..dfe207f7 100644 --- a/src/core/include/units/bits/basic_concepts.h +++ b/src/core/include/units/bits/basic_concepts.h @@ -28,7 +28,7 @@ #include #include #include -#include +#include // IWYU pragma: end_exports #include @@ -56,24 +56,15 @@ void to_prefix_base(const volatile prefix_base*); template concept Prefix = requires(T* t) { detail::to_prefix_base(t); }; -/** - * @brief A concept matching unit's ratio - * - * Satisfied by all ratio values for which `R.num > 0` and `R.den > 0`. - */ -template -concept UnitRatio = (R.num > 0) && (R.den > 0); - // Unit -template - requires UnitRatio +template struct scaled_unit; // TODO: Remove when P1985 accepted namespace detail { -template -void to_base_scaled_unit(const volatile scaled_unit*); +template +void to_base_scaled_unit(const volatile scaled_unit*); } // namespace detail diff --git a/src/core/include/units/bits/common_type.h b/src/core/include/units/bits/common_type.h index 5dd8568b..46f7cb5f 100644 --- a/src/core/include/units/bits/common_type.h +++ b/src/core/include/units/bits/common_type.h @@ -55,22 +55,22 @@ struct common_quantity_reference_impl, reference> { template struct common_quantity_reference_impl, reference> { - using type = reference>; + using type = reference>; }; template requires(same_unit_reference, dimension_unit>::value) struct common_quantity_reference_impl, reference> { - using type = reference>; + using type = reference>; }; template struct common_quantity_reference_impl, reference> { using dimension = conditional, D2, D1>; - static constexpr ratio r1 = D1::base_units_ratio * U1::ratio; - static constexpr ratio r2 = D2::base_units_ratio * U2::ratio; - static constexpr ratio cr = common_ratio(r1, r2); - using unit = downcast_unit; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m1 = D1::base_units_ratio * U1::mag; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto m2 = D2::base_units_ratio * U2::mag; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto cm = common_magnitude(m1, m2); + using unit = downcast_unit; using type = reference; }; diff --git a/src/core/include/units/bits/derived_scaled_unit.h b/src/core/include/units/bits/derived_scaled_unit.h index 5844100e..5a8b4cd7 100644 --- a/src/core/include/units/bits/derived_scaled_unit.h +++ b/src/core/include/units/bits/derived_scaled_unit.h @@ -23,6 +23,7 @@ #pragma once #include +#include namespace units::detail { @@ -35,24 +36,14 @@ inline constexpr bool compatible_units, Us...> = (UnitOf -constexpr ratio inverse_if_negative(const ratio& r) -{ - if constexpr (E::num * E::den > 0) - return r; - else - return inverse(r); -} - template -constexpr ratio derived_ratio(exponent_list) +constexpr Magnitude auto derived_mag(exponent_list) { - return (... * inverse_if_negative( - pow(Us::ratio / dimension_unit::ratio))); + return (as_magnitude<1>() * ... * + pow(Us::mag / dimension_unit::mag)); } template -using derived_scaled_unit = - scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; +using derived_scaled_unit = scaled_unit(typename D::recipe()), typename D::coherent_unit::reference>; } // namespace units::detail diff --git a/src/core/include/units/bits/equivalent.h b/src/core/include/units/bits/equivalent.h index 25d399cb..5bdac612 100644 --- a/src/core/include/units/bits/equivalent.h +++ b/src/core/include/units/bits/equivalent.h @@ -72,8 +72,8 @@ struct equivalent_impl : // additionally accounts for unknown dimensions template struct equivalent_unit : - std::disjunction, std::bool_constant::ratio == - U2::ratio / dimension_unit::ratio>> {}; + std::disjunction, + std::bool_constant::mag == U2::mag / dimension_unit::mag>> {}; // point origins diff --git a/src/core/include/units/bits/external/hacks.h b/src/core/include/units/bits/external/hacks.h index 5a983540..e08faec9 100644 --- a/src/core/include/units/bits/external/hacks.h +++ b/src/core/include/units/bits/external/hacks.h @@ -100,6 +100,16 @@ #endif +#if UNITS_COMP_MSVC + +#define UNITS_MSVC_WORKAROUND(X) + +#else + +#define UNITS_MSVC_WORKAROUND(X) X + +#endif + namespace std { diff --git a/src/core/include/units/bits/unit_text.h b/src/core/include/units/bits/unit_text.h index d0118397..45345468 100644 --- a/src/core/include/units/bits/unit_text.h +++ b/src/core/include/units/bits/unit_text.h @@ -147,7 +147,7 @@ constexpr auto unit_text() }(); constexpr auto prefix_txt = - prefix_or_ratio_text(); + prefix_or_ratio_text(); return prefix_txt + symbol_text; } } diff --git a/src/core/include/units/chrono.h b/src/core/include/units/chrono.h index d19660de..91b4ab6b 100644 --- a/src/core/include/units/chrono.h +++ b/src/core/include/units/chrono.h @@ -33,8 +33,11 @@ namespace units { template struct quantity_like_traits> { +private: + static constexpr auto mag = as_magnitude(); +public: using dimension = isq::si::dim_time; - using unit = downcast_unit; + using unit = downcast_unit; using rep = Rep; [[nodiscard]] static constexpr rep number(const std::chrono::duration& q) { return q.count(); } }; @@ -44,8 +47,11 @@ struct clock_origin : point_origin {}; template struct quantity_point_like_traits>> { +private: + static constexpr auto mag = as_magnitude(); +public: using origin = clock_origin; - using unit = downcast_unit; + using unit = downcast_unit; using rep = Rep; [[nodiscard]] static constexpr auto relative(const std::chrono::time_point>& qp) { @@ -86,13 +92,13 @@ using to_std_ratio = decltype(detail::to_std_ratio_impl()); template [[nodiscard]] constexpr auto to_std_duration(const quantity& q) { - return std::chrono::duration>(q.number()); + return std::chrono::duration>(q.number()); } template [[nodiscard]] constexpr auto to_std_time_point(const quantity_point, U, Rep>& qp) { - using ret_type = std::chrono::time_point>>; + using ret_type = std::chrono::time_point>>; return ret_type(to_std_duration(qp.relative())); } diff --git a/src/core/include/units/derived_dimension.h b/src/core/include/units/derived_dimension.h index 0ea8f23a..cfe0830c 100644 --- a/src/core/include/units/derived_dimension.h +++ b/src/core/include/units/derived_dimension.h @@ -85,7 +85,8 @@ template struct derived_dimension : downcast_dispatch> { using recipe = exponent_list; using coherent_unit = U; - static constexpr ratio base_units_ratio = detail::base_units_ratio(typename derived_dimension::exponents()); + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto base_units_ratio = + detail::absolute_magnitude(typename derived_dimension::exponents()); }; } // namespace units diff --git a/src/core/include/units/generic/dimensionless.h b/src/core/include/units/generic/dimensionless.h index acf9e91d..fc7b97e6 100644 --- a/src/core/include/units/generic/dimensionless.h +++ b/src/core/include/units/generic/dimensionless.h @@ -31,7 +31,7 @@ namespace units { struct one : derived_unit {}; -struct percent : named_scaled_unit {}; +struct percent : named_scaled_unit(), one> {}; /** * @brief Dimension one diff --git a/src/core/include/units/magnitude.h b/src/core/include/units/magnitude.h index 88d003e7..ddf18cce 100644 --- a/src/core/include/units/magnitude.h +++ b/src/core/include/units/magnitude.h @@ -137,13 +137,14 @@ constexpr auto inverse(BasePower auto bp) // `widen_t` gives the widest arithmetic type in the same category, for intermediate computations. template - requires std::is_arithmetic_v -using widen_t = std::conditional_t, long double, - std::conditional_t, std::intmax_t, std::uintmax_t>>; +using widen_t = + std::conditional_t, + std::conditional_t, long double, + std::conditional_t, std::intmax_t, std::uintmax_t>>, + T>; // Raise an arbitrary arithmetic type to a positive integer power at compile time. template - requires std::is_arithmetic_v constexpr T int_power(T base, std::integral auto exp) { // As this function should only be called at compile time, the exceptions herein function as @@ -166,7 +167,7 @@ constexpr T int_power(T base, std::integral auto exp) // template parameter, rather than a function parameter. if (exp == 0) { - return 1; + return T{1}; } if (exp % 2 == 1) { @@ -178,7 +179,6 @@ constexpr T int_power(T base, std::integral auto exp) template - requires std::is_arithmetic_v constexpr widen_t compute_base_power(BasePower auto bp) { // This utility can only handle integer powers. To compute rational powers at compile time, we'll @@ -197,11 +197,11 @@ constexpr widen_t compute_base_power(BasePower auto bp) if constexpr (std::is_integral_v) { throw std::invalid_argument{"Cannot represent reciprocal as integer"}; } else { - return 1 / compute_base_power(inverse(bp)); + return T{1} / compute_base_power(inverse(bp)); } } - auto power = bp.power.num * int_power(10, bp.power.exp); + auto power = numerator(bp.power); return int_power(static_cast>(bp.get_base()), power); } @@ -210,8 +210,8 @@ constexpr widen_t compute_base_power(BasePower auto bp) // The input is the desired result, but in a (wider) intermediate type. The point of this function // is to cast to the desired type, but avoid overflow in doing so. template - requires std::is_arithmetic_v && std::is_arithmetic_v && - (std::is_integral_v == std::is_integral_v) +// TODO(chogg): Migrate this to use `treat_as_floating_point`. + requires(!std::is_integral_v || std::is_integral_v) constexpr To checked_static_cast(From x) { // This function should only ever be called at compile time. The purpose of these exceptions is @@ -220,10 +220,6 @@ constexpr To checked_static_cast(From x) if (!std::in_range(x)) { throw std::invalid_argument{"Cannot represent magnitude in this type"}; } - } else { - if (x < std::numeric_limits::min() || x > std::numeric_limits::max()) { - throw std::invalid_argument{"Cannot represent magnitude in this type"}; - } } return static_cast(x); @@ -374,7 +370,7 @@ struct magnitude { namespace detail { template inline constexpr bool is_magnitude = false; -template +template inline constexpr bool is_magnitude> = true; } // namespace detail @@ -387,8 +383,9 @@ concept Magnitude = detail::is_magnitude; /** * @brief The value of a Magnitude in a desired type T. */ -template - requires std::is_floating_point_v || (std::is_integral_v && is_integral(magnitude{})) +template +// TODO(chogg): Migrate this to use `treat_as_floating_point`. + requires(!std::is_integral_v || is_integral(magnitude{})) constexpr T get_value(const magnitude&) { // Force the expression to be evaluated in a constexpr context, to catch, e.g., overflow. @@ -407,7 +404,7 @@ struct pi_base { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude equality implementation. -template +template constexpr bool operator==(magnitude, magnitude) { if constexpr (sizeof...(LeftBPs) == sizeof...(RightBPs)) { @@ -420,27 +417,39 @@ constexpr bool operator==(magnitude, magnitude) //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude rational powers implementation. -template +template constexpr auto pow(magnitude) { - if constexpr (E == 0) { + if constexpr (E.num == 0) { return magnitude<>{}; } else { return magnitude{}; } } +template +constexpr auto sqrt(magnitude m) +{ + return pow(m); +} + +template +constexpr auto cbrt(magnitude m) +{ + return pow(m); +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Magnitude product implementation. // Base cases, for when either (or both) inputs are the identity. -constexpr auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } -constexpr auto operator*(magnitude<>, Magnitude auto m) { return m; } -constexpr auto operator*(Magnitude auto m, magnitude<>) { return m; } +constexpr Magnitude auto operator*(magnitude<>, magnitude<>) { return magnitude<>{}; } +constexpr Magnitude auto operator*(magnitude<>, Magnitude auto m) { return m; } +constexpr Magnitude auto operator*(Magnitude auto m, magnitude<>) { return m; } // Recursive case for the product of any two non-identity Magnitudes. -template -constexpr auto operator*(magnitude, magnitude) +template +constexpr Magnitude auto operator*(magnitude, magnitude) { // Case for when H1 has the smaller base. if constexpr (H1.get_base() < H2.get_base()) { @@ -486,7 +495,7 @@ constexpr auto operator/(Magnitude auto l, Magnitude auto r) { return l * pow<-1 namespace detail { // The largest integer which can be extracted from any magnitude with only a single basis vector. -template +template constexpr auto integer_part(magnitude) { constexpr auto power_num = numerator(BP.power); @@ -506,7 +515,7 @@ constexpr auto integer_part(magnitude) } // namespace detail -template +template constexpr auto numerator(magnitude) { return (detail::integer_part(magnitude{}) * ... * magnitude<>{}); @@ -525,6 +534,70 @@ constexpr ratio as_ratio(Magnitude auto m) } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Common Magnitude. +// +// The "common Magnitude" C, of two Magnitudes M1 and M2, is the largest Magnitude such that each of its inputs is +// expressible by only positive basis powers relative to C. That is, both (M1 / C) and (M2 / C) contain only positive +// powers in the expansion on our basis. +// +// For rational Magnitudes (or, more precisely, Magnitudes that are rational _relative to each other_), this reduces to +// the familiar convention from the std::chrono library: it is the largest Magnitude C such that each input Magnitude is +// an _integer multiple_ of C. The connection can be seen by considering the definition in the above paragraph, and +// recognizing that both the bases and the powers are all integers for rational Magnitudes. +// +// For relatively _irrational_ Magnitudes (whether from irrational bases, or fractional powers of integer bases), the +// notion of a "common type" becomes less important, because there is no way to preserve pure integer multiplication. +// When we go to retrieve our value, we'll be stuck with a floating point approximation no matter what choice we make. +// Thus, we make the _simplest_ choice which reproduces the correct convention in the rational case: namely, taking the +// minimum power for each base (where absent bases implicitly have a power of 0). + +namespace detail { +template +constexpr auto remove_positive_power(magnitude m) +{ + if constexpr (numerator(BP.power) < 0) { + return m; + } else { + return magnitude<>{}; + } +} + +template +constexpr auto remove_positive_powers(magnitude) +{ + return (magnitude<>{} * ... * remove_positive_power(magnitude{})); +} +} // namespace detail + +// Base cases, for when either (or both) inputs are the identity. +constexpr auto common_magnitude(magnitude<>, magnitude<>) { return magnitude<>{}; } +constexpr auto common_magnitude(magnitude<>, Magnitude auto m) { return detail::remove_positive_powers(m); } +constexpr auto common_magnitude(Magnitude auto m, magnitude<>) { return detail::remove_positive_powers(m); } + +// Recursive case for the common Magnitude of any two non-identity Magnitudes. +template +constexpr auto common_magnitude(magnitude, magnitude) +{ + using detail::remove_positive_power; + + if constexpr (H1.get_base() < H2.get_base()) { + // When H1 has the smaller base, prepend to result from recursion. + return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, magnitude{}); + } else if constexpr (H2.get_base() < H1.get_base()) { + // When H2 has the smaller base, prepend to result from recursion. + return remove_positive_power(magnitude

{}) * common_magnitude(magnitude{}, magnitude{}); + } else { + // When the bases are equal, pick whichever has the lower power. + constexpr auto common_tail = common_magnitude(magnitude{}, magnitude{}); + if constexpr (H1.power < H2.power) { + return magnitude

{} * common_tail; + } else { + return magnitude

{} * common_tail; + } + } +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // `as_magnitude()` implementation. @@ -579,7 +652,7 @@ template requires(R.num > 0) constexpr Magnitude auto as_magnitude() { - return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / + return pow(detail::prime_factorization_v<10>) * detail::prime_factorization_v / detail::prime_factorization_v; } diff --git a/src/core/include/units/math.h b/src/core/include/units/math.h index d2c2728e..0de45139 100644 --- a/src/core/include/units/math.h +++ b/src/core/include/units/math.h @@ -57,7 +57,7 @@ template return rep(1); } else { using dim = dimension_pow; - using unit = downcast_unit(Q::unit::ratio)>; + using unit = downcast_unit(Q::unit::mag)>; using std::pow; return quantity( static_cast(pow(q.number(), static_cast(Num) / static_cast(Den)))); @@ -77,7 +77,7 @@ template requires requires { sqrt(q.number()); } || requires { std::sqrt(q.number()); } { using dim = dimension_pow; - using unit = downcast_unit; + using unit = downcast_unit(Q::unit::mag)>; using rep = TYPENAME Q::rep; using std::sqrt; return quantity(static_cast(sqrt(q.number()))); @@ -96,7 +96,7 @@ template requires requires { cbrt(q.number()); } || requires { std::cbrt(q.number()); } { using dim = dimension_pow; - using unit = downcast_unit; + using unit = downcast_unit(Q::unit::mag)>; using rep = TYPENAME Q::rep; using std::cbrt; return quantity(static_cast(cbrt(q.number()))); diff --git a/src/core/include/units/prefix.h b/src/core/include/units/prefix.h index 901a50bd..0a95499c 100644 --- a/src/core/include/units/prefix.h +++ b/src/core/include/units/prefix.h @@ -25,6 +25,7 @@ #include #include // IWYU pragma: begin_exports +#include #include #include // IWYU pragma: end_exports @@ -35,7 +36,7 @@ namespace detail { template struct prefix_base : downcast_base> { - static constexpr ::units::ratio ratio = R; + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = as_magnitude(); }; } // namespace detail diff --git a/src/core/include/units/quantity.h b/src/core/include/units/quantity.h index d2a40fa2..6f9f9d0c 100644 --- a/src/core/include/units/quantity.h +++ b/src/core/include/units/quantity.h @@ -407,7 +407,7 @@ public: { gsl_ExpectsAudit(q.number() != quantity_values::zero()); using dim = dim_invert; - using ret_unit = downcast_unit; + using ret_unit = downcast_unit(U::mag)>; using ret = quantity, Value, rep>>; return ret(v / q.number()); } diff --git a/src/core/include/units/quantity_cast.h b/src/core/include/units/quantity_cast.h index e4f8cb62..fdb90aef 100644 --- a/src/core/include/units/quantity_cast.h +++ b/src/core/include/units/quantity_cast.h @@ -27,6 +27,7 @@ #include #include #include +#include UNITS_DIAGNOSTIC_PUSH // warning C4244: 'argument': conversion from 'intmax_t' to 'T', possible loss of data with T=int @@ -49,25 +50,28 @@ class quantity_point_kind; namespace detail { template -inline constexpr ratio quantity_ratio = std::enable_if_t>{}; +inline constexpr Magnitude auto quantity_magnitude = std::enable_if_t, magnitude<>>{}; template -inline constexpr ratio quantity_ratio> = [] { +inline constexpr Magnitude auto quantity_magnitude> = [] { if constexpr (BaseDimension) { - return U::ratio; + return U::mag; } else { - return D::base_units_ratio * U::ratio / D::coherent_unit::ratio; + return D::base_units_ratio * U::mag / D::coherent_unit::mag; } }(); +template +inline constexpr ratio quantity_ratio = as_ratio(quantity_magnitude); + template -inline constexpr ratio cast_ratio = [] { +inline constexpr Magnitude auto cast_magnitude = [] { using FromU = TYPENAME QFrom::unit; using ToU = TYPENAME QTo::unit; if constexpr (same_unit_reference::value) { - return FromU::ratio / ToU::ratio; + return FromU::mag / ToU::mag; } else { - return quantity_ratio / quantity_ratio; + return quantity_magnitude / quantity_magnitude; } }(); @@ -112,25 +116,14 @@ template R using traits = detail::cast_traits; using ratio_type = TYPENAME traits::ratio_type; using rep_type = TYPENAME traits::rep_type; - constexpr auto c_ratio = detail::cast_ratio, To>; - if constexpr (treat_as_floating_point) { - return To( - static_cast(static_cast(q.number()) * - (static_cast(c_ratio.num) * detail::fpow10(c_ratio.exp) / - static_cast(c_ratio.den)))); - } else { - if constexpr (c_ratio.exp > 0) { - return To(static_cast( - static_cast(q.number()) * - (static_cast(c_ratio.num) * static_cast(detail::ipow10(c_ratio.exp))) / - static_cast(c_ratio.den))); - } else { - return To(static_cast( - static_cast(q.number()) * static_cast(c_ratio.num) / - (static_cast(c_ratio.den) * static_cast(detail::ipow10(-c_ratio.exp))))); - } - } + constexpr Magnitude auto c_mag = detail::cast_magnitude, To>; + constexpr Magnitude auto num = numerator(c_mag); + constexpr Magnitude auto den = denominator(c_mag); + constexpr Magnitude auto irr = c_mag * (den / num); + + constexpr auto val = [](Magnitude auto m) { return get_value(m); }; + return To(static_cast(static_cast(q.number()) * val(num) / val(den) * val(irr))); } /** @@ -149,7 +142,7 @@ template requires equivalent [[nodiscard]] constexpr auto quantity_cast(const quantity& q) { - return quantity_cast, Rep>>(q); + return quantity_cast, Rep>>(q); } /** diff --git a/src/core/include/units/ratio.h b/src/core/include/units/ratio.h index 0fb2f428..b57ce06d 100644 --- a/src/core/include/units/ratio.h +++ b/src/core/include/units/ratio.h @@ -32,6 +32,7 @@ #include #include +#include #include namespace units { @@ -60,6 +61,8 @@ struct ratio { [[nodiscard]] friend constexpr bool operator==(const ratio&, const ratio&) = default; + [[nodiscard]] friend constexpr auto operator<=>(const ratio& lhs, const ratio& rhs) { return (lhs - rhs).num <=> 0; } + [[nodiscard]] friend constexpr ratio operator-(const ratio& r) { return ratio(-r.num, r.den, r.exp); } [[nodiscard]] friend constexpr ratio operator+(ratio lhs, ratio rhs) @@ -78,6 +81,8 @@ struct ratio { return ratio{lhs.num * rhs.den + lhs.den * rhs.num, lhs.den * rhs.den, common_exp}; } + [[nodiscard]] friend constexpr ratio operator-(const ratio& lhs, const ratio& rhs) { return lhs + (-rhs); } + [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); diff --git a/src/core/include/units/reference.h b/src/core/include/units/reference.h index cca74b79..200cc2fb 100644 --- a/src/core/include/units/reference.h +++ b/src/core/include/units/reference.h @@ -37,13 +37,13 @@ namespace detail { template using reference_multiply_impl = - reference::ratio) * (U2::ratio / dimension_unit::ratio) * - dimension_unit::ratio>>; + reference::mag) * (U2::mag / dimension_unit::mag) * + dimension_unit::mag>>; template using reference_divide_impl = - reference::ratio) / (U2::ratio / dimension_unit::ratio) * - dimension_unit::ratio>>; + reference::mag) / (U2::mag / dimension_unit::mag) * + dimension_unit::mag>>; } // namespace detail diff --git a/src/core/include/units/unit.h b/src/core/include/units/unit.h index ad79bae3..52b9779c 100644 --- a/src/core/include/units/unit.h +++ b/src/core/include/units/unit.h @@ -28,6 +28,7 @@ // IWYU pragma: begin_exports #include #include +#include #include #include #include @@ -54,20 +55,17 @@ inline constexpr bool can_be_prefixed = false; * (i.e. all length units are expressed in terms of meter, all mass units are expressed in * terms of gram, ...) * - * @tparam R a ratio of a reference unit + * @tparam M a Magnitude representing the (relative) size of this unit * @tparam U a unit to use as a reference for this dimension */ -template - requires UnitRatio -struct scaled_unit : downcast_base> { - static constexpr ::units::ratio ratio = R; +template +struct scaled_unit : downcast_base> { + static constexpr UNITS_MSVC_WORKAROUND(Magnitude) auto mag = M; using reference = U; }; -template -// template // TODO: GCC crash!!! - requires UnitRatio -using downcast_unit = downcast::reference>>; +template +using downcast_unit = downcast::reference>>; template struct same_unit_reference : is_same {}; @@ -82,7 +80,7 @@ struct same_unit_reference : is_same -struct named_unit : downcast_dispatch> { +struct named_unit : downcast_dispatch(), Child>> { static constexpr auto symbol = Symbol; }; @@ -94,12 +92,11 @@ struct named_unit : downcast_dispatch> { * * @tparam Child inherited class type used by the downcasting facility (CRTP Idiom) * @tparam Symbol a short text representation of the unit - * @tparam R a scale to apply to U + * @tparam M the Magnitude by which to scale U * @tparam U a reference unit to scale */ -template - requires UnitRatio -struct named_scaled_unit : downcast_dispatch> { +template +struct named_scaled_unit : downcast_dispatch> { static constexpr auto symbol = Symbol; }; @@ -116,7 +113,7 @@ struct named_scaled_unit : downcast_dispatch requires detail::can_be_prefixed -struct prefixed_unit : downcast_dispatch> { +struct prefixed_unit : downcast_dispatch> { static constexpr auto symbol = P::symbol + U::symbol; }; @@ -129,7 +126,7 @@ struct prefixed_unit : downcast_dispatch -struct derived_unit : downcast_dispatch> {}; +struct derived_unit : downcast_dispatch(), Child>> {}; /** * @brief A unit with a deduced ratio and symbol @@ -198,8 +195,8 @@ namespace detail { template void is_named_impl(const volatile named_unit*); -template -void is_named_impl(const volatile named_scaled_unit*); +template +void is_named_impl(const volatile named_scaled_unit*); template void is_named_impl(const volatile prefixed_unit*); @@ -216,8 +213,8 @@ inline constexpr bool is_named = requires(U * u) { is_named_impl(u); }; template void can_be_prefixed_impl(const volatile named_unit*); -template -void can_be_prefixed_impl(const volatile named_scaled_unit*); +template +void can_be_prefixed_impl(const volatile named_scaled_unit*); template void can_be_prefixed_impl(const volatile alias_unit*); @@ -225,8 +222,8 @@ void can_be_prefixed_impl(const volatile alias_unit*); template inline constexpr bool can_be_prefixed = requires(U * u) { can_be_prefixed_impl(u); }; -template -inline constexpr bool can_be_prefixed> = can_be_prefixed; +template +inline constexpr bool can_be_prefixed> = can_be_prefixed; } // namespace detail diff --git a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h index f88b5710..eaf7a7ca 100644 --- a/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h +++ b/src/systems/isq-iec80000/include/units/isq/iec80000/storage_capacity.h @@ -53,7 +53,7 @@ struct tebibit : prefixed_unit {}; struct pebibit : prefixed_unit {}; struct exbibit : prefixed_unit {}; -struct byte : named_scaled_unit {}; +struct byte : named_scaled_unit(), bit> {}; struct kilobyte : prefixed_unit {}; struct megabyte : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/force.h b/src/systems/si-fps/include/units/isq/si/fps/force.h index 19f52527..a026bd79 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/force.h +++ b/src/systems/si-fps/include/units/isq/si/fps/force.h @@ -40,7 +40,7 @@ namespace units::isq::si::fps { struct poundal : named_unit {}; // https://en.wikipedia.org/wiki/Pound_(force) -struct pound_force : named_scaled_unit {}; +struct pound_force : named_scaled_unit(), poundal> {}; struct kilopound_force : prefixed_unit {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/length.h b/src/systems/si-fps/include/units/isq/si/fps/length.h index 8f307136..04a9e930 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/length.h +++ b/src/systems/si-fps/include/units/isq/si/fps/length.h @@ -39,6 +39,7 @@ using si::international::fathom; using si::international::foot; using si::international::inch; using si::international::mil; +using si::international::mile; using si::international::thou; using si::international::yard; @@ -47,9 +48,7 @@ struct thousandth : alias_unit {}; struct kiloyard : prefixed_unit {}; -struct mile : named_scaled_unit {}; - -struct nautical_mile : named_scaled_unit {}; +struct nautical_mile : named_scaled_unit(), yard> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si-fps/include/units/isq/si/fps/mass.h b/src/systems/si-fps/include/units/isq/si/fps/mass.h index 2891c8bc..16b23f0a 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/mass.h +++ b/src/systems/si-fps/include/units/isq/si/fps/mass.h @@ -35,28 +35,28 @@ namespace units::isq::si::fps { // https://en.wikipedia.org/wiki/Pound_(mass) -struct pound : named_scaled_unit {}; +struct pound : named_scaled_unit(), si::kilogram> {}; struct dim_mass : isq::dim_mass {}; template U, Representation Rep = double> using mass = quantity; -struct grain : named_scaled_unit {}; +struct grain : named_scaled_unit(), pound> {}; -struct dram : named_scaled_unit {}; +struct dram : named_scaled_unit(), pound> {}; -struct ounce : named_scaled_unit {}; +struct ounce : named_scaled_unit(), pound> {}; -struct stone : named_scaled_unit {}; +struct stone : named_scaled_unit(), pound> {}; -struct quarter : named_scaled_unit {}; +struct quarter : named_scaled_unit(), pound> {}; -struct hundredweight : named_scaled_unit {}; +struct hundredweight : named_scaled_unit(), pound> {}; -struct short_ton : named_scaled_unit {}; +struct short_ton : named_scaled_unit(), pound> {}; -struct long_ton : named_scaled_unit {}; +struct long_ton : named_scaled_unit(), pound> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-fps/include/units/isq/si/fps/power.h b/src/systems/si-fps/include/units/isq/si/fps/power.h index 8126702f..931c08f4 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/power.h +++ b/src/systems/si-fps/include/units/isq/si/fps/power.h @@ -42,7 +42,7 @@ struct dim_power : isq::dim_power {}; -struct horse_power : named_scaled_unit {}; +struct horse_power : named_scaled_unit(), foot_pound_force_per_second> {}; template U, Representation Rep = double> using power = quantity; diff --git a/src/systems/si-fps/include/units/isq/si/fps/pressure.h b/src/systems/si-fps/include/units/isq/si/fps/pressure.h index 31128c33..4b036fc1 100644 --- a/src/systems/si-fps/include/units/isq/si/fps/pressure.h +++ b/src/systems/si-fps/include/units/isq/si/fps/pressure.h @@ -44,10 +44,11 @@ template U, Representation Rep = double> using pressure = quantity; struct pound_force_per_foot_sq : - named_scaled_unit {}; + named_scaled_unit(), + poundal_per_foot_sq> {}; struct pound_force_per_inch_sq : - named_scaled_unit {}; + named_scaled_unit(), pound_force_per_foot_sq> {}; struct kilopound_force_per_inch_sq : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/area.h b/src/systems/si-hep/include/units/isq/si/hep/area.h index dc8e5895..e08b59eb 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/area.h +++ b/src/systems/si-hep/include/units/isq/si/hep/area.h @@ -37,7 +37,7 @@ namespace units::isq::si::hep { // effective cross-sectional area according to EU council directive 80/181/EEC // https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:01980L0181-20090527#page=10 // https://www.fedlex.admin.ch/eli/cc/1994/3109_3109_3109/de -struct barn : named_scaled_unit {}; +struct barn : named_scaled_unit(), square_metre> {}; struct yocto_barn : prefixed_unit {}; struct zepto_barn : prefixed_unit {}; struct atto_barn : prefixed_unit {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/mass.h b/src/systems/si-hep/include/units/isq/si/hep/mass.h index 10de3a3f..e4a895e0 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/mass.h +++ b/src/systems/si-hep/include/units/isq/si/hep/mass.h @@ -32,11 +32,19 @@ #include #include +// Necessary to factor `1'672'621'923'695`, which appears in the proton mass. +template<> +inline constexpr std::optional units::known_first_factor<334'524'384'739> = 334'524'384'739; + +// Necessary to factor `17'826'619'216'279`, which appears in the value for eV/c^2. +template<> +inline constexpr std::optional units::known_first_factor<225'653'407'801> = 225'653'407'801; + namespace units::isq::si::hep { struct eV_per_c2 : named_scaled_unit {}; + as_magnitude(), kilogram> {}; struct feV_per_c2 : prefixed_unit {}; struct peV_per_c2 : prefixed_unit {}; struct neV_per_c2 : prefixed_unit {}; @@ -52,10 +60,11 @@ struct PeV_per_c2 : prefixed_unit {}; struct EeV_per_c2 : prefixed_unit {}; struct YeV_per_c2 : prefixed_unit {}; struct electron_mass : - named_scaled_unit {}; -struct proton_mass : named_scaled_unit {}; + named_scaled_unit(), kilogram> {}; +struct proton_mass : + named_scaled_unit(), kilogram> {}; struct neutron_mass : - named_scaled_unit {}; + named_scaled_unit(), kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si-hep/include/units/isq/si/hep/momentum.h b/src/systems/si-hep/include/units/isq/si/hep/momentum.h index 83557347..c6791501 100644 --- a/src/systems/si-hep/include/units/isq/si/hep/momentum.h +++ b/src/systems/si-hep/include/units/isq/si/hep/momentum.h @@ -32,12 +32,17 @@ #include #include +// Necessary to factor `5'344'285'992'678`, which appears in the value for eV/c. +template<> +inline constexpr std::optional units::known_first_factor<296'904'777'371> = 157'667; + namespace units::isq::si::hep { struct kilogram_metre_per_second : derived_unit {}; struct eV_per_c : - named_scaled_unit {}; + named_scaled_unit(), + kilogram_metre_per_second> {}; struct feV_per_c : prefixed_unit {}; struct peV_per_c : prefixed_unit {}; struct neV_per_c : prefixed_unit {}; diff --git a/src/systems/si-iau/include/units/isq/si/iau/length.h b/src/systems/si-iau/include/units/isq/si/iau/length.h index 1c50ccc1..d2e184a1 100644 --- a/src/systems/si-iau/include/units/isq/si/iau/length.h +++ b/src/systems/si-iau/include/units/isq/si/iau/length.h @@ -36,13 +36,13 @@ namespace units::isq::si::iau { // https://en.wikipedia.org/wiki/Light-year -struct light_year : named_scaled_unit {}; +struct light_year : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Parsec -struct parsec : named_scaled_unit {}; +struct parsec : named_scaled_unit(), si::metre> {}; // https://en.wikipedia.org/wiki/Angstrom -struct angstrom : named_scaled_unit {}; +struct angstrom : named_scaled_unit(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-imperial/include/units/isq/si/imperial/length.h b/src/systems/si-imperial/include/units/isq/si/imperial/length.h index 8721ae4d..5f4be435 100644 --- a/src/systems/si-imperial/include/units/isq/si/imperial/length.h +++ b/src/systems/si-imperial/include/units/isq/si/imperial/length.h @@ -35,10 +35,10 @@ namespace units::isq::si::imperial { // https://en.wikipedia.org/wiki/Chain_(unit) -struct chain : named_scaled_unit {}; +struct chain : named_scaled_unit(), si::international::yard> {}; // https://en.wikipedia.org/wiki/Rod_(unit) -struct rod : named_scaled_unit {}; +struct rod : named_scaled_unit(), chain> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-international/include/units/isq/si/international/length.h b/src/systems/si-international/include/units/isq/si/international/length.h index 4f93feae..b61bd2cf 100644 --- a/src/systems/si-international/include/units/isq/si/international/length.h +++ b/src/systems/si-international/include/units/isq/si/international/length.h @@ -37,30 +37,30 @@ namespace units::isq::si::international { // si::international yard // https://en.wikipedia.org/wiki/International_yard_and_pound -struct yard : named_scaled_unit {}; +struct yard : named_scaled_unit(), si::metre> {}; // si::international foot // https://en.wikipedia.org/wiki/Foot_(unit)#International_foot -struct foot : named_scaled_unit {}; +struct foot : named_scaled_unit(), yard> {}; // https://en.wikipedia.org/wiki/Fathom#International_fathom -struct fathom : named_scaled_unit {}; +struct fathom : named_scaled_unit(), yard> {}; // si::international inch // https://en.wikipedia.org/wiki/Inch#Equivalences -struct inch : named_scaled_unit {}; +struct inch : named_scaled_unit(), yard> {}; // intrnational mile // https://en.wikipedia.org/wiki/Mile#International_mile -struct mile : named_scaled_unit {}; +struct mile : named_scaled_unit(), si::kilometre> {}; // si::international nautical mile // https://en.wikipedia.org/wiki/Nautical_mile -struct nautical_mile : named_scaled_unit {}; +struct nautical_mile : named_scaled_unit(), si::metre> {}; // thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch -struct thou : named_scaled_unit {}; +struct thou : named_scaled_unit(), inch> {}; // mil - different name for thou // https://en.wikipedia.org/wiki/Thousandth_of_an_inch diff --git a/src/systems/si-typographic/include/units/isq/si/typographic/length.h b/src/systems/si-typographic/include/units/isq/si/typographic/length.h index 5e88390d..e69c31c6 100644 --- a/src/systems/si-typographic/include/units/isq/si/typographic/length.h +++ b/src/systems/si-typographic/include/units/isq/si/typographic/length.h @@ -37,10 +37,12 @@ namespace units::isq::si::typographic { // TODO Conflicts with (https://en.wikipedia.org/wiki/Pica_(typography)), verify correctness of below conversion factors // and provide hyperlinks to definitions -struct pica_comp : named_scaled_unit {}; -struct pica_prn : named_scaled_unit {}; -struct point_comp : named_scaled_unit {}; -struct point_prn : named_scaled_unit {}; +struct pica_comp : + named_scaled_unit(), si::metre> {}; +struct pica_prn : named_scaled_unit(), si::metre> {}; +struct point_comp : + named_scaled_unit(), si::metre> {}; +struct point_prn : named_scaled_unit(), si::metre> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si-uscs/include/units/isq/si/uscs/length.h b/src/systems/si-uscs/include/units/isq/si/uscs/length.h index 8c09a57d..fe4eb9f1 100644 --- a/src/systems/si-uscs/include/units/isq/si/uscs/length.h +++ b/src/systems/si-uscs/include/units/isq/si/uscs/length.h @@ -36,14 +36,14 @@ namespace units::isq::si::uscs { // https://en.wikipedia.org/wiki/Foot_(unit)#US_survey_foot // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct foot : named_scaled_unit {}; +struct foot : named_scaled_unit(), si::metre> {}; // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct fathom : named_scaled_unit {}; +struct fathom : named_scaled_unit(), foot> {}; // https://en.wikipedia.org/wiki/Mile#U.S._survey_mile // https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors#B6 -struct mile : named_scaled_unit {}; +struct mile : named_scaled_unit(), foot> {}; #ifndef UNITS_NO_LITERALS diff --git a/src/systems/si/include/units/isq/si/catalytic_activity.h b/src/systems/si/include/units/isq/si/catalytic_activity.h index d52e81ac..07ca426f 100644 --- a/src/systems/si/include/units/isq/si/catalytic_activity.h +++ b/src/systems/si/include/units/isq/si/catalytic_activity.h @@ -58,7 +58,7 @@ struct exakatal : prefixed_unit {}; struct zettakatal : prefixed_unit {}; struct yottakatal : prefixed_unit {}; -struct enzyme_unit : named_scaled_unit {}; +struct enzyme_unit : named_scaled_unit(), katal> {}; struct dim_catalytic_activity : isq::dim_catalytic_activity {}; diff --git a/src/systems/si/include/units/isq/si/energy.h b/src/systems/si/include/units/isq/si/energy.h index cb841b4b..cf0eba26 100644 --- a/src/systems/si/include/units/isq/si/energy.h +++ b/src/systems/si/include/units/isq/si/energy.h @@ -55,7 +55,8 @@ struct yottajoule : prefixed_unit {}; // N.B. electron charge (and eV) is an exact constant: // https://www.bipm.org/documents/20126/41483022/SI-Brochure-9.pdf#page=147 -struct electronvolt : named_scaled_unit {}; +struct electronvolt : + named_scaled_unit(), joule> {}; struct gigaelectronvolt : prefixed_unit {}; struct dim_energy : isq::dim_energy {}; diff --git a/src/systems/si/include/units/isq/si/length.h b/src/systems/si/include/units/isq/si/length.h index 0e7f28fd..aefebdbb 100644 --- a/src/systems/si/include/units/isq/si/length.h +++ b/src/systems/si/include/units/isq/si/length.h @@ -56,7 +56,7 @@ struct exametre : prefixed_unit {}; struct zettametre : prefixed_unit {}; struct yottametre : prefixed_unit {}; -struct astronomical_unit : named_scaled_unit {}; +struct astronomical_unit : named_scaled_unit(), metre> {}; struct dim_length : isq::dim_length {}; diff --git a/src/systems/si/include/units/isq/si/luminous_flux.h b/src/systems/si/include/units/isq/si/luminous_flux.h index 7d9592e3..f2b30290 100644 --- a/src/systems/si/include/units/isq/si/luminous_flux.h +++ b/src/systems/si/include/units/isq/si/luminous_flux.h @@ -36,7 +36,7 @@ namespace units::isq::si { // TODO Is this correct? Should we account for steradian here? How? -struct lumen : named_scaled_unit {}; +struct lumen : named_scaled_unit(), watt> {}; using dim_luminous_flux = dim_power; diff --git a/src/systems/si/include/units/isq/si/magnetic_induction.h b/src/systems/si/include/units/isq/si/magnetic_induction.h index a1a7f507..74bd7ca6 100644 --- a/src/systems/si/include/units/isq/si/magnetic_induction.h +++ b/src/systems/si/include/units/isq/si/magnetic_induction.h @@ -56,7 +56,7 @@ struct exatesla : prefixed_unit {}; struct zettatesla : prefixed_unit {}; struct yottatesla : prefixed_unit {}; -struct gauss : named_scaled_unit {}; +struct gauss : named_scaled_unit(), tesla> {}; struct dim_magnetic_induction : isq::dim_magnetic_induction {}; diff --git a/src/systems/si/include/units/isq/si/mass.h b/src/systems/si/include/units/isq/si/mass.h index dd877f26..53a574f4 100644 --- a/src/systems/si/include/units/isq/si/mass.h +++ b/src/systems/si/include/units/isq/si/mass.h @@ -78,7 +78,8 @@ struct exatonne : prefixed_alias_unit {}; struct zettatonne : prefixed_unit {}; struct yottatonne : prefixed_unit {}; -struct dalton : named_scaled_unit {}; +struct dalton : + named_scaled_unit(), kilogram> {}; struct dim_mass : isq::dim_mass {}; diff --git a/src/systems/si/include/units/isq/si/time.h b/src/systems/si/include/units/isq/si/time.h index 7c855957..17f47804 100644 --- a/src/systems/si/include/units/isq/si/time.h +++ b/src/systems/si/include/units/isq/si/time.h @@ -43,9 +43,9 @@ struct picosecond : prefixed_unit {}; struct nanosecond : prefixed_unit {}; struct microsecond : prefixed_unit {}; struct millisecond : prefixed_unit {}; -struct minute : named_scaled_unit {}; -struct hour : named_scaled_unit {}; -struct day : named_scaled_unit {}; +struct minute : named_scaled_unit(), second> {}; +struct hour : named_scaled_unit(), minute> {}; +struct day : named_scaled_unit(), hour> {}; struct dim_time : isq::dim_time {}; diff --git a/test/unit_test/runtime/fmt_test.cpp b/test/unit_test/runtime/fmt_test.cpp index 70c8c73f..7cc0f748 100644 --- a/test/unit_test/runtime/fmt_test.cpp +++ b/test/unit_test/runtime/fmt_test.cpp @@ -84,7 +84,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") { SECTION("in terms of base units") { - const length> q(123); + const length(as_magnitude<10>()), metre>> q(123); os << q; SECTION("iostream") { CHECK(os.str() == "123 Mm"); } @@ -96,7 +96,7 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") SECTION("in terms of derived units") { - const energy> q(60); + const energy(as_magnitude<10>()), joule>> q(60); os << q; SECTION("iostream") { CHECK(os.str() == "60 cJ"); } @@ -257,7 +257,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 60_q_kJ / 2_q_min; os << q; - SECTION("iostream") { CHECK(os.str() == "30 [1/6 × 10²] W"); } + // TODO(chogg): Reinstate after format/Magnitude design. + // SECTION("iostream") { CHECK(os.str() == "30 [1/6 × 10²] W"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } @@ -390,7 +391,8 @@ TEST_CASE("operator<< on a quantity", "[text][ostream][fmt]") const auto q = 60_q_min / 2_q_km; os << q; - SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m⋅s"); } + // TODO(chogg): Reinstate after format/Magnitude design. + // SECTION("iostream") { CHECK(os.str() == "30 [6 × 10⁻²] 1/m ⋅ s"); } SECTION("fmt with default format {} on a quantity") { CHECK(STD_FMT::format("{}", q) == os.str()); } diff --git a/test/unit_test/runtime/fmt_units_test.cpp b/test/unit_test/runtime/fmt_units_test.cpp index f862bffd..0f611eed 100644 --- a/test/unit_test/runtime/fmt_units_test.cpp +++ b/test/unit_test/runtime/fmt_units_test.cpp @@ -30,6 +30,7 @@ #include #include #include +#include using namespace units::isq::si; using namespace units::isq::si::references; @@ -65,7 +66,7 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") CHECK(STD_FMT::format("{}", 1_q_fathom_us) == "1 fathom(us)"); CHECK(STD_FMT::format("{}", 1_q_mi) == "1 mi"); CHECK(STD_FMT::format("{}", 1_q_mi_us) == "1 mi(us)"); - CHECK(STD_FMT::format("{}", 1_q_naut_mi) == "1 nmi"); + CHECK(STD_FMT::format("{}", 1_q_naut_mi) == "1 mi(naut)"); CHECK(STD_FMT::format("{}", 1_q_ch) == "1 ch"); CHECK(STD_FMT::format("{}", 1_q_rd) == "1 rd"); CHECK(STD_FMT::format("{}", 1_q_thou) == "1 thou"); @@ -315,16 +316,19 @@ TEST_CASE("std::format on synthesized unit symbols", "[text][fmt]") SECTION("incoherent units with powers") { - CHECK(STD_FMT::format("{}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 × 10⁹] m³"); - CHECK(STD_FMT::format("{}", 1_q_au * 1_q_au) == "1 [2237952291797391849 × 10⁴] m²"); - - CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 x 10^9] m^3"); - CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_au * 1_q_au) == "1 [2237952291797391849 x 10^4] m^2"); + // TODO(chogg): Reinstate after format/Magnitude redesign. + // CHECK(STD_FMT::format("{}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 × 10⁹] m³"); + // CHECK(STD_FMT::format("{}", 1_q_au * 1_q_au) == "1 [2237952291797391849 × 10⁴] m²"); + // + // CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_mi * 1_q_mi * 1_q_mi) == "1 [15900351812136/3814697265625 x 10^9] m^3"); + // CHECK(STD_FMT::format("{:%Q %Aq}", 1_q_au * 1_q_au) == "1 [2237952291797391849 x 10^4] m^2"); } SECTION("unknown scaled unit with reference different than the dimension's coherent unit") { - CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); - CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); + // TODO(chogg): Reinstate after format/Magnitude redesign. + // constexpr auto mag = units::as_magnitude(); + // CHECK(STD_FMT::format("{}", mass>(1)) == "1 [2/3 × 10⁻³] kg"); + // CHECK(STD_FMT::format("{:%Q %Aq}", mass>(1)) == "1 [2/3 x 10^-3] kg"); } } diff --git a/test/unit_test/runtime/magnitude_test.cpp b/test/unit_test/runtime/magnitude_test.cpp index e5d46f26..0715df8f 100644 --- a/test/unit_test/runtime/magnitude_test.cpp +++ b/test/unit_test/runtime/magnitude_test.cpp @@ -292,6 +292,27 @@ TEST_CASE("Multiplication works for magnitudes") } } +TEST_CASE("Common Magnitude") +{ + SECTION("Identity for identical magnitudes") + { + CHECK(common_magnitude(as_magnitude<1>(), as_magnitude<1>()) == as_magnitude<1>()); + CHECK(common_magnitude(as_magnitude<15>(), as_magnitude<15>()) == as_magnitude<15>()); + CHECK(common_magnitude(pi_to_the(), pi_to_the()) == pi_to_the()); + } + + SECTION("Greatest Common Factor for integers") + { + CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<36>()) == as_magnitude<12>()); + CHECK(common_magnitude(as_magnitude<24>(), as_magnitude<37>()) == as_magnitude<1>()); + } + + SECTION("Handles fractions") + { + CHECK(common_magnitude(as_magnitude(), as_magnitude()) == as_magnitude()); + } +} + TEST_CASE("Division works for magnitudes") { SECTION("Dividing anything by itself reduces to null magnitude") diff --git a/test/unit_test/static/cgs_test.cpp b/test/unit_test/static/cgs_test.cpp index 35dfd87b..4a0dc7bb 100644 --- a/test/unit_test/static/cgs_test.cpp +++ b/test/unit_test/static/cgs_test.cpp @@ -53,7 +53,7 @@ static_assert(10_q_cm == 2_q_cm_per_s * 5_q_s); static_assert(detail::unit_text() == "cm/s"); // area -static_assert(centimetre::ratio / dimension_unit::ratio == ratio(1)); +static_assert(as_ratio(centimetre::mag / dimension_unit::mag) == ratio(1)); static_assert((1_q_cm * 1_q_cm).number() == 1); static_assert((1_q_cm2).number() == 1); diff --git a/test/unit_test/static/concepts_test.cpp b/test/unit_test/static/concepts_test.cpp index b0e62788..2e9d66d1 100644 --- a/test/unit_test/static/concepts_test.cpp +++ b/test/unit_test/static/concepts_test.cpp @@ -47,15 +47,6 @@ using namespace units::isq; static_assert(Prefix); static_assert(!Prefix); -// UnitRatio - -static_assert(UnitRatio); -static_assert(!UnitRatio); -// static_assert(UnitRatio); // static_assert in ratio -static_assert(UnitRatio); -static_assert(!UnitRatio); -static_assert(!UnitRatio); - // BaseDimension static_assert(BaseDimension); diff --git a/test/unit_test/static/fps_test.cpp b/test/unit_test/static/fps_test.cpp index c86961c6..774971e7 100644 --- a/test/unit_test/static/fps_test.cpp +++ b/test/unit_test/static/fps_test.cpp @@ -52,7 +52,7 @@ static_assert(10_q_ft == 2_q_ft_per_s * 5_q_s); static_assert(detail::unit_text() == "ft/s"); // area -static_assert(foot::ratio / dimension_unit::ratio == ratio(1)); +static_assert(as_ratio(foot::mag / dimension_unit::mag) == ratio(1)); static_assert(1_q_ft * 1_q_ft == 1_q_ft2); static_assert(100_q_ft2 / 10_q_ft == 10_q_ft); @@ -61,7 +61,7 @@ static_assert(detail::unit_text() == basic_symbol_text("f // volume static_assert(1_q_yd * 1_q_yd * 1_q_yd == 1_q_yd3); -static_assert(cubic_yard::ratio / cubic_foot::ratio == ratio(27)); +static_assert(as_ratio(cubic_yard::mag / cubic_foot::mag) == ratio(27)); /* ************** DERIVED DIMENSIONS WITH NAMED UNITS **************** */ diff --git a/test/unit_test/static/quantity_kind_test.cpp b/test/unit_test/static/quantity_kind_test.cpp index e8658e2b..c421ec12 100644 --- a/test/unit_test/static/quantity_kind_test.cpp +++ b/test/unit_test/static/quantity_kind_test.cpp @@ -219,7 +219,7 @@ static_assert(same(quantity_kind(rate_of_climb(0.01 static_assert(construct_from_only>(1).common() == 1); static_assert(construct_from_only>(1.0).common() == 1); -static_assert(construct_from_only>(1ULL).common().number() == 1); +static_assert(construct_from_only>(1LL).common().number() == 1); static_assert(construct_from_only>(1.0L).common().number() == 1); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); @@ -456,9 +456,9 @@ concept invalid_compound_assignments = requires !requires { w *= m; }; requires !requires { w /= m; }; requires !requires { w %= m; }; - requires !requires { w *= quantity_kind, scaled_unit, int>{1}; }; - requires !requires { w /= quantity_kind, scaled_unit, int>{1}; }; - requires !requires { w %= quantity_kind, scaled_unit, int>{1}; }; + requires !requires { w *= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w /= quantity_kind, scaled_unit(), one>, int>{1}; }; + requires !requires { w %= quantity_kind, scaled_unit(), one>, int>{1}; }; requires !requires { w %= 1.0; }; requires !requires { w %= quantity(1.0); }; requires !requires { w %= 1.0 * (w / w); }; diff --git a/test/unit_test/static/quantity_point_kind_test.cpp b/test/unit_test/static/quantity_point_kind_test.cpp index f8febd8e..9a234a29 100644 --- a/test/unit_test/static/quantity_point_kind_test.cpp +++ b/test/unit_test/static/quantity_point_kind_test.cpp @@ -264,7 +264,7 @@ static_assert(construct_from_only>(1).relative().common() static_assert(construct_from_only>(short{1}).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); static_assert(construct_from_only>(1).relative().common() == 1); -static_assert(construct_from_only>(1ULL).relative().common().number() == 1); +static_assert(construct_from_only>(1LL).relative().common().number() == 1); static_assert(construct_from_only>(1).relative().common().number() == 1); static_assert(!constructible_or_convertible_from>(1.0)); static_assert(!constructible_or_convertible_from>(1.0)); diff --git a/test/unit_test/static/quantity_test.cpp b/test/unit_test/static/quantity_test.cpp index 82bf34d0..cf346a27 100644 --- a/test/unit_test/static/quantity_test.cpp +++ b/test/unit_test/static/quantity_test.cpp @@ -498,7 +498,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency, std::int64_t>>); + frequency(), hertz>, std::int64_t>>); static_assert(is_same_v); static_assert(is_same_v); @@ -529,7 +529,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1._q_s), - frequency, long double>>); + frequency(), hertz>, long double>>); static_assert(compare>); static_assert(compare>); static_assert(compare(1)), length>); @@ -551,7 +551,7 @@ static_assert(compare> static_assert(compare>); static_assert(compare>); static_assert(compare(1) / 1_q_s), - frequency, long double>>); + frequency(), hertz>, long double>>); // different units static_assert(compare>); @@ -579,22 +579,25 @@ static_assert(is_same_v>); -static_assert(compare, std::int64_t>>); +static_assert( + compare(), metre>, std::int64_t>>); static_assert( compare, exponent>, - scaled_unit, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); static_assert(compare>); -static_assert(compare, std::int64_t>>); -static_assert(compare>); static_assert( - compare>, - scaled_unit, std::int64_t>>); -static_assert(compare, std::int64_t>>); + compare(), hertz>, std::int64_t>>); +static_assert(compare>); +static_assert(compare>, + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); +static_assert(compare(), one>, std::int64_t>>); static_assert(compare>); -static_assert(compare, std::int64_t>>); +static_assert( + compare(), metre_per_second>, std::int64_t>>); static_assert( compare, exponent>, - scaled_unit, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); static_assert((1_q_m + 1_q_m).number() == 2); static_assert((1_q_m + 1_q_km).number() == 1001); @@ -884,8 +887,9 @@ static_assert(!is_same_v(2_q_dm3)), volume, units::exponent>, - scaled_unit, std::int64_t>>); -static_assert(is_same_v, std::int64_t>>); + scaled_unit(), unknown_coherent_unit>, std::int64_t>>); +static_assert( + is_same_v(), metre>, std::int64_t>>); #else @@ -915,7 +919,8 @@ static_assert(same(quotient_remainder_theorem(3'000 * m, 400), 3'000 * m)); static_assert(comp(quotient_remainder_theorem(3'000 * m, quantity(400)), 3'000 * m)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(400)), 3 * km)); static_assert(comp(quotient_remainder_theorem(3 * km, quantity(2)), 3 * km)); -static_assert(comp(quotient_remainder_theorem(3 * km, dimensionless, int>(400)), - 3 * km)); +static_assert( + comp(quotient_remainder_theorem(3 * km, dimensionless(), one>, int>(400)), + 3 * km)); } // namespace diff --git a/test/unit_test/static/ratio_test.cpp b/test/unit_test/static/ratio_test.cpp index 4abe5094..ad966950 100644 --- a/test/unit_test/static/ratio_test.cpp +++ b/test/unit_test/static/ratio_test.cpp @@ -108,4 +108,10 @@ static_assert(numerator(ratio(3, 7, 2)) == 300); static_assert(denominator(ratio(3, 4)) == 4); static_assert(denominator(ratio(3, 7, -2)) == 700); +// comparison +static_assert((ratio(3, 4) <=> ratio(6, 8)) == (0 <=> 0)); +static_assert((ratio(3, 4) <=> ratio(-3, 4)) == (0 <=> -1)); +static_assert((ratio(-3, 4) <=> ratio(3, -4)) == (0 <=> 0)); +static_assert((ratio(1, 1, 1) <=> ratio(10)) == (0 <=> 0)); + } // namespace diff --git a/test/unit_test/static/si_fps_test.cpp b/test/unit_test/static/si_fps_test.cpp index a3b0aeb5..bb023c8c 100644 --- a/test/unit_test/static/si_fps_test.cpp +++ b/test/unit_test/static/si_fps_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include namespace { @@ -113,6 +114,13 @@ static_assert(1_q_pdl_per_ft2 > 1.4881639435_q_Pa && 1_q_pdl_per_ft2 < 1.4881639 } // namespace fps_plus_si_literals namespace fps_test { +namespace { +constexpr bool is_near(auto a, auto b, auto tol) +{ + const auto diff = a - b; + return (diff <= tol) && (-diff <= tol); +} +} // namespace using namespace units::isq::si::fps::literals; using namespace units::isq::si::fps::references; @@ -121,10 +129,12 @@ using namespace units::isq::si::fps::references; static_assert(si::length(1) + 1 * ft == si::length(1.3048)); static_assert(1 * ft + si::length(1) == si::length(1.3048)); -static_assert(quantity_cast>(1. * ft / 0.3048) + si::length(1) == - si::length(2)); // 1 m in ft + 1 m -static_assert(si::length(1) + quantity_cast>(1. * ft / 0.3048) == - si::length(2)); // 1 m + 1 m in ft +static_assert(is_near(quantity_cast>(1. * ft / 0.3048) + si::length(1), + si::length(2), + si::length(1))); // 1 m in ft + 1 m +static_assert(is_near(si::length(1) + quantity_cast>(1. * ft / 0.3048), + si::length(2), + si::length(1))); // 1 m + 1 m in ft static_assert(1 * ft + quantity_cast>(si::length(0.3048)) == 2 * ft); // 1 ft + 1 ft in m static_assert(quantity_cast>(si::length(0.3048)) + 1 * ft == diff --git a/test/unit_test/static/si_test.cpp b/test/unit_test/static/si_test.cpp index 48a7d24e..02960463 100644 --- a/test/unit_test/static/si_test.cpp +++ b/test/unit_test/static/si_test.cpp @@ -43,7 +43,7 @@ static_assert(1_q_au == 149'597'870'700_q_m); static_assert(1_q_km + 1_q_m == 1001_q_m); static_assert(10_q_km / 5_q_km == 2); static_assert(10_q_km / 5_q_km < 3); -static_assert(100_q_mm / 5_q_cm == dimensionless>(20)); +static_assert(100_q_mm / 5_q_cm == dimensionless(), one>>(20)); static_assert(100_q_mm / 5_q_cm == dimensionless(2)); static_assert(10_q_km / 2 == 5_q_km); @@ -107,7 +107,7 @@ static_assert(1000 / 1_q_s == 1_q_kHz); static_assert(1 / 1_q_ms == 1_q_kHz); static_assert(3.2_q_GHz == 3'200'000'000_q_Hz); static_assert((10_q_Hz * 1_q_min).number() == 10); -static_assert(10_q_Hz * 1_q_min == dimensionless>(10)); +static_assert(10_q_Hz * 1_q_min == dimensionless(), one>>(10)); static_assert(10_q_Hz * 1_q_min == dimensionless(600)); static_assert(2 / 1_q_Hz == 2_q_s); diff --git a/test/unit_test/static/unit_test.cpp b/test/unit_test/static/unit_test.cpp index 9ec78e22..128fac3d 100644 --- a/test/unit_test/static/unit_test.cpp +++ b/test/unit_test/static/unit_test.cpp @@ -36,12 +36,12 @@ using namespace units::isq; struct metre : named_unit {}; struct centimetre : prefixed_unit {}; struct kilometre : prefixed_unit {}; -struct yard : named_scaled_unit {}; -struct foot : named_scaled_unit {}; +struct yard : named_scaled_unit(), metre> {}; +struct foot : named_scaled_unit(), yard> {}; struct dim_length : base_dimension<"length", metre> {}; struct second : named_unit {}; -struct hour : named_scaled_unit {}; +struct hour : named_scaled_unit(), second> {}; struct dim_time : base_dimension<"time", second> {}; struct kelvin : named_unit {}; @@ -59,17 +59,11 @@ struct kilometre_per_hour : derived_scaled_unit); static_assert(equivalent); -static_assert(compare>, metre>); -static_assert(compare>, centimetre>); -static_assert(compare>, yard>); -static_assert(compare>, foot>); -static_assert(compare>, kilometre_per_hour>); - -#if !UNITS_COMP_MSVC -static_assert([]() { - return !requires { typename scaled_unit; }; -}.template operator()()); // negative unit ratio -#endif +static_assert(compare(), metre>>, metre>); +static_assert(compare(), metre>>, centimetre>); +static_assert(compare>, yard>); +static_assert(compare(), metre>>, foot>); +static_assert(compare>, kilometre_per_hour>); static_assert(centimetre::symbol == "cm"); static_assert(kilometre::symbol == "km");