mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
feat: value_cast<Unit, Representation>
added
This commit is contained in:
@ -6,6 +6,7 @@
|
||||
|
||||
- feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by @NAThompson
|
||||
- feat: `quantity_point` support added for `quantity_cast` and `value_cast`
|
||||
- feat: `value_cast<Unit, Representation>` added
|
||||
- (!) refactor: `zero_Fahrenheit` renamed to `zeroth_Fahrenheit`
|
||||
- refactor: math functions constraints refactored
|
||||
- fix: `QuantityLike` conversions required `Q::rep` instead of using one provided by `quantity_like_traits`
|
||||
|
@ -83,7 +83,7 @@ Next, let's do the same for integral and floating-point representations, but thi
|
||||
using US Customary units:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="54"
|
||||
--8<-- "example/avg_speed.cpp:93:125"
|
||||
--8<-- "example/avg_speed.cpp:93:124"
|
||||
```
|
||||
|
||||
One important difference here is the fact that as it is not possible to make a lossless conversion
|
||||
@ -108,8 +108,8 @@ Please note how the first and third results get truncated using integral represe
|
||||
|
||||
In the end, we repeat the scenario for CGS units:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="87"
|
||||
--8<-- "example/avg_speed.cpp:127:157"
|
||||
```cpp title="avg_speed.cpp" linenums="86"
|
||||
--8<-- "example/avg_speed.cpp:126:155"
|
||||
```
|
||||
|
||||
Again, we observe `value_cast` being used in the same places and consistent truncation errors
|
||||
@ -129,6 +129,6 @@ Average speed of a car that makes 2.2e+07 cm in 7200 s is 110 km/h.
|
||||
|
||||
The example file ends with a simple `main()` function:
|
||||
|
||||
```cpp title="avg_speed.cpp" linenums="118"
|
||||
--8<-- "example/avg_speed.cpp:159:"
|
||||
```cpp title="avg_speed.cpp" linenums="116"
|
||||
--8<-- "example/avg_speed.cpp:157:"
|
||||
```
|
||||
|
@ -77,3 +77,76 @@ quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);
|
||||
It is often fine to use an integral as a representation type, but in general, floating-point
|
||||
types provide better precision and are privileged in the library as they are considered
|
||||
to be value-preserving.
|
||||
|
||||
In some cases, a unit and a representation type should be changed simultaneously. Moreover,
|
||||
sometimes, the order of doing those operations matters. In such cases, the library provides
|
||||
the `value_cast<U, Rep>(q)` which always returns the most precise result:
|
||||
|
||||
=== "C++23"
|
||||
|
||||
```cpp
|
||||
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
|
||||
inline constexpr struct currency : quantity_spec<dim_currency> {} currency;
|
||||
|
||||
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
|
||||
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
|
||||
|
||||
namespace unit_symbols {
|
||||
|
||||
inline constexpr auto USD = us_dollar;
|
||||
inline constexpr auto USD_s = scaled_us_dollar;
|
||||
|
||||
} // namespace unit_symbols
|
||||
|
||||
using Price = quantity<currency[us_dollar], double>;
|
||||
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
|
||||
|
||||
Price price = 12.95 * USD;
|
||||
Scaled spx = value_cast<USD_s, std::int64_t>(price);
|
||||
```
|
||||
|
||||
=== "C++20"
|
||||
|
||||
```cpp
|
||||
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
|
||||
inline constexpr struct currency : quantity_spec<currency, dim_currency> {} currency;
|
||||
|
||||
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
|
||||
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
|
||||
|
||||
namespace unit_symbols {
|
||||
|
||||
inline constexpr auto USD = us_dollar;
|
||||
inline constexpr auto USD_s = scaled_us_dollar;
|
||||
|
||||
} // namespace unit_symbols
|
||||
|
||||
using Price = quantity<currency[us_dollar], double>;
|
||||
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
|
||||
|
||||
Price price = 12.95 * USD;
|
||||
Scaled spx = value_cast<USD_s, std::int64_t>(price);
|
||||
```
|
||||
|
||||
=== "Portable"
|
||||
|
||||
```cpp
|
||||
inline constexpr struct dim_currency : base_dimension<"$"> {} dim_currency;
|
||||
QUANTITY_SPEC(currency, dim_currency);
|
||||
|
||||
inline constexpr struct us_dollar : named_unit<"USD", kind_of<currency>> {} us_dollar;
|
||||
inline constexpr struct scaled_us_dollar : named_unit<"USD_s", mag_power<10, -8> * us_dollar> {} scaled_us_dollar;
|
||||
|
||||
namespace unit_symbols {
|
||||
|
||||
inline constexpr auto USD = us_dollar;
|
||||
inline constexpr auto USD_s = scaled_us_dollar;
|
||||
|
||||
} // namespace unit_symbols
|
||||
|
||||
using Price = quantity<currency[us_dollar], double>;
|
||||
using Scaled = quantity<currency[scaled_us_dollar], std::int64_t>;
|
||||
|
||||
Price price = 12.95 * USD;
|
||||
Scaled spx = value_cast<USD_s, std::int64_t>(price);
|
||||
```
|
||||
|
@ -118,8 +118,7 @@ void example()
|
||||
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
|
||||
// also it is not possible to make a lossless conversion of miles to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(distance, duration,
|
||||
fixed_int_si_avg_speed(value_cast<int>(distance.force_in(m)), value_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
}
|
||||
@ -148,8 +147,7 @@ void example()
|
||||
// conversion from a floating-point to an integral type is a truncating one so an explicit cast is needed
|
||||
// it is not possible to make a lossless conversion of centimeters to meters on an integral type
|
||||
// (explicit cast needed)
|
||||
print_result(distance, duration,
|
||||
fixed_int_si_avg_speed(value_cast<int>(distance.force_in(m)), value_cast<int>(duration)));
|
||||
print_result(distance, duration, fixed_int_si_avg_speed(value_cast<m, int>(distance), value_cast<int>(duration)));
|
||||
|
||||
print_result(distance, duration, fixed_double_si_avg_speed(distance, duration));
|
||||
print_result(distance, duration, avg_speed(distance, duration));
|
||||
|
@ -75,6 +75,26 @@ template<Representation ToRep, typename Q>
|
||||
return detail::sudo_cast<quantity<std::remove_reference_t<Q>::reference, ToRep>>(std::forward<Q>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity's unit and representation type
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (e.g. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* auto q = value_cast<us, int>(1.23 * ms);
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, Representation ToRep, typename Q>
|
||||
requires Quantity<std::remove_cvref_t<Q>> && (convertible(std::remove_reference_t<Q>::reference, ToU)) &&
|
||||
RepresentationOf<ToRep, std::remove_reference_t<Q>::quantity_spec.character> &&
|
||||
std::constructible_from<ToRep, typename std::remove_reference_t<Q>::rep>
|
||||
[[nodiscard]] constexpr Quantity auto value_cast(Q&& q)
|
||||
{
|
||||
using q_type = std::remove_reference_t<Q>;
|
||||
return detail::sudo_cast<quantity<q_type::quantity_spec[ToU], ToRep>>(std::forward<Q>(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity point's unit
|
||||
*
|
||||
@ -112,4 +132,24 @@ template<Representation ToRep, typename QP>
|
||||
return {value_cast<ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_), qp.point_origin};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Explicit cast of a quantity's unit and representation type
|
||||
*
|
||||
* Implicit conversions between quantities of different types are allowed only for "safe"
|
||||
* (e.g. non-truncating) conversion. In truncating cases an explicit cast have to be used.
|
||||
*
|
||||
* auto q = value_cast<us, int>(1.23 * ms);
|
||||
*
|
||||
* @tparam ToRep a representation type to use for a target quantity
|
||||
*/
|
||||
template<Unit auto ToU, Representation ToRep, typename QP>
|
||||
requires QuantityPoint<std::remove_cvref_t<QP>> && (convertible(std::remove_reference_t<QP>::reference, ToU)) &&
|
||||
RepresentationOf<ToRep, std::remove_reference_t<QP>::quantity_spec.character> &&
|
||||
std::constructible_from<ToRep, typename std::remove_reference_t<QP>::rep>
|
||||
[[nodiscard]] constexpr QuantityPoint auto value_cast(QP&& qp)
|
||||
{
|
||||
return quantity_point{value_cast<ToU, ToRep>(std::forward<QP>(qp).quantity_from_origin_is_an_implementation_detail_),
|
||||
qp.point_origin};
|
||||
}
|
||||
|
||||
} // namespace mp_units
|
||||
|
Reference in New Issue
Block a user