diff --git a/src/core/include/mp-units/bits/sudo_cast.h b/src/core/include/mp-units/bits/sudo_cast.h index 2ce05e7f..65efb882 100644 --- a/src/core/include/mp-units/bits/sudo_cast.h +++ b/src/core/include/mp-units/bits/sudo_cast.h @@ -30,17 +30,12 @@ namespace mp_units::detail { -// determines the best available representation type -template -[[nodiscard]] consteval auto common_rep_type(From, To) -{ - if constexpr (requires { typename std::common_type_t; }) - // returns a common type of two representation types if available - // e.g. `double` and `int` will end up with `double` precision - return std::common_type_t{}; - else - return typename From::rep{}; -} +template +struct get_common_type : std::common_type {}; + +template +using maybe_common_type = MP_UNITS_TYPENAME std::conditional_t; }, + get_common_type, std::type_identity>::type; /** * @brief Explicit cast between different quantity types @@ -73,15 +68,29 @@ template constexpr Magnitude auto num = numerator(c_mag); constexpr Magnitude auto den = denominator(c_mag); constexpr Magnitude auto irr = c_mag * (den / num); - using c_rep_type = decltype(common_rep_type(q, To{})); + using c_rep_type = maybe_common_type::rep, typename To::rep>; using c_mag_type = common_magnitude_type; - using multiplier_type = - conditional, std::common_type_t, c_mag_type>; + using multiplier_type = conditional, + // ensure that the multiplier is also floating-point + conditional>, + // reuse user's type if possible + std::common_type_t>, + std::common_type_t>, + c_mag_type>; + using c_type = maybe_common_type; constexpr auto val = [](Magnitude auto m) { return get_value(m); }; - return {static_cast( - static_cast(std::forward(q).numerical_value_is_an_implementation_detail_) * val(num) / - val(den) * val(irr)), - To::reference}; + if constexpr (std::is_floating_point_v) { + // this results in great assembly + constexpr auto ratio = val(num) / val(den) * val(irr); + auto res = static_cast( + static_cast(q.numerical_value_is_an_implementation_detail_) * ratio); + return {res, To::reference}; + } else { + // this is slower but allows conversions like 2000 m -> 2 km without loosing data + auto res = static_cast( + static_cast(q.numerical_value_is_an_implementation_detail_) * val(num) / val(den) * val(irr)); + return {res, To::reference}; + } } }