2023-06-21 10:55:18 +02:00
|
|
|
# Value Conversions
|
|
|
|
|
|
|
|
## Value-preserving conversions
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
auto q1 = 5 * km;
|
2023-08-23 16:46:15 +02:00
|
|
|
std::cout << q1.in(m) << '\n';
|
2023-06-21 10:55:18 +02:00
|
|
|
quantity<si::metre, int> q2 = q1;
|
|
|
|
```
|
|
|
|
|
2023-12-26 11:07:21 +01:00
|
|
|
The second line above converts the current quantity to the one expressed in meters and prints its
|
|
|
|
contents. The third line converts the quantity expressed in kilometers into the one measured
|
|
|
|
in meters.
|
2023-06-21 10:55:18 +02:00
|
|
|
|
|
|
|
In case a user would like to perform an opposite transformation:
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
auto q1 = 5 * m;
|
2023-08-23 16:46:15 +02:00
|
|
|
std::cout << q1.in(km) << '\n';
|
2023-06-21 10:55:18 +02:00
|
|
|
quantity<si::kilo<si::metre>, int> q2 = q1;
|
|
|
|
```
|
|
|
|
|
|
|
|
Both conversions will fail to compile.
|
|
|
|
|
|
|
|
There are two ways to make the above work. The first solution is to use a floating-point
|
|
|
|
representation type:
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
auto q1 = 5. * m;
|
2023-08-23 16:46:15 +02:00
|
|
|
std::cout << q1.in(km) << '\n';
|
2023-06-21 10:55:18 +02:00
|
|
|
quantity<si::kilo<si::metre>> q2 = q1;
|
|
|
|
```
|
|
|
|
|
2023-09-13 09:00:21 +02:00
|
|
|
or
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
auto q1 = 5 * m;
|
|
|
|
std::cout << value_cast<double>(q1).in(km) << '\n';
|
|
|
|
quantity<si::kilo<si::metre>> q2 = q1; // double by default
|
|
|
|
```
|
|
|
|
|
2023-08-31 18:57:39 +02:00
|
|
|
!!! important
|
|
|
|
|
|
|
|
The **mp-units** library follows [`std::chrono::duration`](https://en.cppreference.com/w/cpp/chrono/duration)
|
|
|
|
logic and treats floating-point types as value-preserving.
|
2023-06-21 10:55:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
## Value-truncating conversions
|
|
|
|
|
|
|
|
The second solution is to force a truncating conversion:
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
auto q1 = 5 * m;
|
|
|
|
std::cout << value_cast<km>(q1) << '\n';
|
2023-09-13 10:44:50 +02:00
|
|
|
quantity<si::kilo<si::metre>, int> q2 = q1.force_in(km);
|
2023-06-21 10:55:18 +02:00
|
|
|
```
|
|
|
|
|
|
|
|
This explicit cast makes it clear that something unsafe is going on. It is easy to spot in code
|
|
|
|
reviews or while chasing a bug in the source code.
|
|
|
|
|
2023-09-13 10:44:50 +02:00
|
|
|
!!! note
|
|
|
|
|
|
|
|
`q.force_in(U)` is just a shortcut to run `value_cast<U>(q)`. There is no difference in behavior
|
|
|
|
between those two interfaces. `q.force_in(U)` was added for consistency with `q.in(U)` and
|
|
|
|
`q.force_numerical_value_in(U)`.
|
|
|
|
|
2023-06-21 10:55:18 +02:00
|
|
|
Another place where this cast is useful is when a user wants to convert a quantity with
|
2023-12-26 11:07:21 +01:00
|
|
|
a floating-point representation to the one using an integral one. Again, this is a truncating
|
2023-06-21 10:55:18 +02:00
|
|
|
conversion, so an explicit cast is needed:
|
|
|
|
|
|
|
|
```cpp
|
|
|
|
quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);
|
|
|
|
```
|
|
|
|
|
|
|
|
!!! info
|
|
|
|
|
2023-12-26 11:07:21 +01:00
|
|
|
It is often OK to use an integral as a representation type, but in general, floating-point
|
2023-06-21 10:55:18 +02:00
|
|
|
types provide better precision and are privileged in the library as they are considered
|
|
|
|
to be value-preserving.
|
2023-12-19 18:19:22 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2024-01-08 10:30:45 +01:00
|
|
|
using Price = quantity_point<currency[us_dollar]>;
|
|
|
|
using Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;
|
2023-12-19 18:19:22 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
=== "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
|
|
|
|
|
2024-01-08 10:30:45 +01:00
|
|
|
using Price = quantity_point<currency[us_dollar]>;
|
|
|
|
using Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;
|
2023-12-19 18:19:22 +01:00
|
|
|
```
|
|
|
|
|
|
|
|
=== "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
|
|
|
|
|
2024-01-08 10:30:45 +01:00
|
|
|
using Price = quantity_point<currency[us_dollar]>;
|
|
|
|
using Scaled = quantity_point<currency[scaled_us_dollar], zeroth_point_origin<currency>, std::int64_t>;
|
2023-12-19 18:19:22 +01:00
|
|
|
```
|
2024-02-27 09:43:41 +01:00
|
|
|
|
|
|
|
```cpp
|
|
|
|
using namespace unit_symbols;
|
|
|
|
Price price{12.95 * USD};
|
|
|
|
Scaled spx = value_cast<USD_s, std::int64_t>(price);
|
|
|
|
```
|