mirror of
https://github.com/mpusz/mp-units.git
synced 2025-06-25 01:01:33 +02:00
docs: "Framework Basics" chapters updated and cleaned up
This commit is contained in:
@ -4,7 +4,7 @@
|
||||
|
||||
### 2.2.0 <small>WIP</small> { id="2.2.0" }
|
||||
|
||||
- feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by @NAThompson
|
||||
- feat: `fma`, `isfinite`, `isinf`, and `isnan` math function added by [@NAThompson](https://github.com/NAThompson)
|
||||
- feat: `quantity_point` support added for `quantity_cast` and `value_cast`
|
||||
- feat: `value_cast<Unit, Representation>` added
|
||||
- (!) refactor: `zero_Fahrenheit` renamed to `zeroth_degree_Fahrenheit`
|
||||
@ -15,6 +15,7 @@
|
||||
- docs: project blog and first posts added
|
||||
- docs: project documentation layout refactored
|
||||
- docs: "Interoperability with Other Libraries" chapter added
|
||||
- docs: "Framework Basics" chapters updated and cleaned up
|
||||
|
||||
### 2.1.0 <small>December 9, 2023</small> { id="2.1.0" }
|
||||
|
||||
|
@ -18,8 +18,8 @@
|
||||
Such distinction is important because each quantity character represents different properties
|
||||
and allows different operations to be done on its quantities.
|
||||
|
||||
For example, imagine a physical units library that allows the creation of a `speed` quantity from both
|
||||
`length / time` and `length * time`. It wouldn't be too safe to use such a product, right?
|
||||
For example, imagine a physical units library that allows the creation of a $speed$ quantity from both
|
||||
$length / time$ and $length * time$. It wouldn't be too safe to use such a product, right?
|
||||
|
||||
Now we have to realize that both of the above operations (multiplication and division) are not even
|
||||
mathematically defined for linear algebra types such as vectors or tensors. On the other hand, two vectors
|
||||
@ -34,36 +34,36 @@ results from both cases. This simply can't work.
|
||||
While defining quantities ISO 80000 explicitly mentions when a specific quantity has a vector or tensor
|
||||
character. Here are some examples:
|
||||
|
||||
| Quantity | Character | Quantity Equation |
|
||||
|------------------------|:------------:|:-------------------------------------------------:|
|
||||
| `duration` | scalar | _{base quantity}_ |
|
||||
| `mass` | scalar | _{base quantity}_ |
|
||||
| `length` | scalar | _{base quantity}_ |
|
||||
| `path_length` | scalar | _{base quantity}_ |
|
||||
| `radius` | scalar | _{base quantity}_ |
|
||||
| `position_vector` | **vector** | _{base quantity}_ |
|
||||
| `velocity` | **vector** | `position_vector / duration` |
|
||||
| `acceleration` | **vector** | `velocity / duration` |
|
||||
| `force` | **vector** | `mass * acceleration` |
|
||||
| `power` | scalar | `force ⋅ velocity` |
|
||||
| `moment_of_force` | **vector** | `position_vector × force` |
|
||||
| `torque` | scalar | `moment_of_force ⋅ {unit-vector}` |
|
||||
| `surface_tension` | scalar | `|force| / length` |
|
||||
| `angular_displacement` | scalar | `path_length / radius` |
|
||||
| `angular_velocity` | **vector** | `angular_displacement / duration * {unit-vector}` |
|
||||
| `momentum` | **vector** | `mass * velocity` |
|
||||
| `angular_momentum` | **vector** | `position_vector × momentum` |
|
||||
| `moment_of_inertia` | **_tensor_** | `angular_momentum ⊗ angular_velocity` |
|
||||
| Quantity | Character | Quantity Equation |
|
||||
|--------------------------|:------------:|:-------------------------------------------------------:|
|
||||
| $duration$ | scalar | _{base quantity}_ |
|
||||
| $mass$ | scalar | _{base quantity}_ |
|
||||
| $length$ | scalar | _{base quantity}_ |
|
||||
| $path\; length$ | scalar | _{base quantity}_ |
|
||||
| $radius$ | scalar | _{base quantity}_ |
|
||||
| $position\; vector$ | **vector** | _{base quantity}_ |
|
||||
| $velocity$ | **vector** | $position\; vector / duration$ |
|
||||
| $acceleration$ | **vector** | $velocity / duration$ |
|
||||
| $force$ | **vector** | $mass * acceleration$ |
|
||||
| $power$ | scalar | $force \cdot velocity$ |
|
||||
| $moment\; of\; force$ | **vector** | $position\; vector \times force$ |
|
||||
| $torque$ | scalar | $moment\; of\; force \cdot \{unit\; vector\}$ |
|
||||
| $surface\; tension$ | scalar | $\lvert force \rvert / length$ |
|
||||
| $angular\; displacement$ | scalar | $path\; length / radius$ |
|
||||
| $angular\; velocity$ | **vector** | $angular\; displacement / duration * \{unit\; vector\}$ |
|
||||
| $momentum$ | **vector** | $mass * velocity$ |
|
||||
| $angular\; momentum$ | **vector** | $position\; vector \times momentum$ |
|
||||
| $moment\; of\; inertia$ | **_tensor_** | $angular\; momentum \otimes angular\; velocity$ |
|
||||
|
||||
In the above equations:
|
||||
|
||||
- `a * b` - regular multiplication where one of the arguments has to be scalar
|
||||
- `a / b` - regular division where the divisor has to be scalar
|
||||
- `a ⋅ b` - dot product of two vectors
|
||||
- `a × b` - cross product of two vectors
|
||||
- `|a|` - magnitude of a vector
|
||||
- `{unit-vector}` - a special vector with the magnitude of `1`
|
||||
- `a ⊗ b` - tensor product of two vectors or tensors
|
||||
- $a * b$ - regular multiplication where one of the arguments has to be scalar
|
||||
- $a / b$ - regular division where the divisor has to be scalar
|
||||
- $a \cdot b$ - dot product of two vectors
|
||||
- $a \times b$ - cross product of two vectors
|
||||
- $\lvert a \rvert$ - magnitude of a vector
|
||||
- $\{unit\; vector\}$ - a special vector with the magnitude of $1$
|
||||
- $a \otimes b$ - tensor product of two vectors or tensors
|
||||
|
||||
!!! note
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
The quantities we discussed so far always had some specific type and physical dimension.
|
||||
However, this is not always the case. While performing various computations, we sometimes end up with
|
||||
so-called "dimensionless" quantities, which ISO correctly defines as
|
||||
so-called "dimensionless" quantities, which ISO defines as
|
||||
[quantities of dimension one](../../appendix/glossary.md#dimensionless-quantity):
|
||||
|
||||
!!! quote "ISO/IEC Guide 99"
|
||||
@ -70,8 +70,8 @@ static_assert(q.quantity_spec == isq::work / isq::heat);
|
||||
|
||||
As shown above, the result is not of a `dimensionless` type anymore. Instead, we get a quantity type
|
||||
derived from the performed [quantity equation](../../appendix/glossary.md#quantity-equation).
|
||||
According to the [ISQ](../../appendix/glossary.md#isq), work divided by heat is the recipe for
|
||||
the thermodynamic efficiency quantity, thus:
|
||||
According to the [ISQ](../../appendix/glossary.md#isq), _work_ divided by _heat_ is the recipe for
|
||||
the _thermodynamic efficiency_ quantity, thus:
|
||||
|
||||
```cpp
|
||||
static_assert(implicitly_convertible(q.quantity_spec, isq::efficiency_thermodynamics));
|
||||
@ -91,7 +91,7 @@ Now, let's see what happens when we divide two quantities of the same type but d
|
||||
constexpr QuantityOf<dimensionless> auto q = isq::height(4 * km) / isq::height(2 * m);
|
||||
```
|
||||
|
||||
This time we still get a quantity of `dimensionless` type with a `dimension_one` as its dimension.
|
||||
This time, we still get a quantity of the `dimensionless` type with a `dimension_one` as its dimension.
|
||||
However, the resulting unit is not `one` anymore:
|
||||
|
||||
```cpp
|
||||
@ -101,16 +101,16 @@ static_assert(q.unit == mag_power<10, 3> * one);
|
||||
In case we would print the text output of this quantity, we would not see a raw value of `2000`,
|
||||
but `2 km/m`.
|
||||
|
||||
First, it may look surprising, but this is actually consistent with the division of quantities
|
||||
First, it may look surprising, but this is consistent with dividing quantities
|
||||
of different dimensions. For example, if we divide `4 * km / 2 * s`, we do not expect `km` to be
|
||||
"expanded" to `m` before the division, right? We would expect the result of `2 km/s`, which is
|
||||
exactly what we get when we divide quantities of the same kind.
|
||||
|
||||
This is a compelling feature that allows us to express huge or tiny ratios without the need
|
||||
for big and expensive representation types. With this, we can easily define things like
|
||||
a [Hubble's constant](https://en.wikipedia.org/wiki/Hubble%27s_law#Dimensionless_Hubble_constant)
|
||||
a [_Hubble's constant_](https://en.wikipedia.org/wiki/Hubble%27s_law#Dimensionless_Hubble_constant)
|
||||
that uses a unit that is proportional to the ratio of kilometers per megaparsecs, which are both
|
||||
units of length:
|
||||
units of _length_:
|
||||
|
||||
```cpp
|
||||
inline constexpr struct hubble_constant :
|
||||
@ -123,12 +123,12 @@ inline constexpr struct hubble_constant :
|
||||
Another important use case for dimensionless quantities is to provide strong types for counts
|
||||
of things. For example:
|
||||
|
||||
- ISO-80000-3 provides a `rotation` quantity defined as the number of revolutions,
|
||||
- IEC-80000-6 provides a `number_of_turns_in_a_winding` quantity,
|
||||
- IEC-80000-13 provides a `Hamming_distance` quantity defined as the number of digit positions
|
||||
- ISO-80000-3 provides a _rotation_ quantity defined as the number of revolutions,
|
||||
- IEC-80000-6 provides a _number of turns in a winding_ quantity,
|
||||
- IEC-80000-13 provides a _Hamming distance_ quantity defined as the number of digit positions
|
||||
in which the corresponding digits of two words of the same length are different.
|
||||
|
||||
Thanks to assigning strong names to such quantities, later on they can be explicitly used as
|
||||
Thanks to assigning strong names to such quantities, later on, they can be explicitly used as
|
||||
arguments in the [quantity equations](../../appendix/glossary.md#quantity-equation) of other
|
||||
quantities deriving from them.
|
||||
|
||||
@ -165,17 +165,17 @@ inline constexpr struct per_mille : named_unit<basic_symbol_text{"‰", "%o"}, m
|
||||
|
||||
## Angular quantities
|
||||
|
||||
Special, often controversial, examples of dimensionless quantities are an angular measure
|
||||
and solid angular measure quantities that are defined in the [ISQ](../../appendix/glossary.md#isq)
|
||||
to be the result of a division of `arc_length / radius` and `area / pow<2>(radius)` respectively.
|
||||
Special, often controversial, examples of dimensionless quantities are an _angular measure_
|
||||
and _solid angular measure_ quantities that are defined in the [ISQ](../../appendix/glossary.md#isq)
|
||||
to be the result of a division of $arc\; length / radius$ and $area / radius^2$ respectively.
|
||||
Moreover, [ISQ](../../appendix/glossary.md#isq) also explicitly states that both can be
|
||||
expressed in the unit `one`. This means that both `isq::angular_measure` and `isq::solid_angular_measure`
|
||||
should be of a [kind](../../appendix/glossary.md#kind) of `dimensionless`.
|
||||
expressed in the unit `one`. This means that both _angular measure_ and _solid angular measure_
|
||||
should be of a [kind](../../appendix/glossary.md#kind) dimensionless.
|
||||
|
||||
On the other hand, [ISQ](../../appendix/glossary.md#isq) also specifies that a unit `radian` can
|
||||
be used for `isq::angular_measure`, and a unit `steradian` can be used for `isq::solid_angular_measure`.
|
||||
On the other hand, [ISQ](../../appendix/glossary.md#isq) also specifies that a unit radian can
|
||||
be used for _angular measure_, and a unit steradian can be used for _solid angular measure_.
|
||||
Those should not be mixed or used to express other types of dimensionless quantities. This means
|
||||
that both `isq::angular_measure` and `isq::solid_angular_measure` should also be
|
||||
that both _angular measure_ and _solid angular measure_ should also be
|
||||
[quantity kinds](../../appendix/glossary.md#kind) by themselves.
|
||||
|
||||
!!! note
|
||||
@ -187,8 +187,8 @@ that both `isq::angular_measure` and `isq::solid_angular_measure` should also be
|
||||
|
||||
## Nested quantity kinds
|
||||
|
||||
Angular quantities are not the only ones with such a "strange" behavior. Another, but a similar case
|
||||
is a `storage_capacity` quantity specified in IEC-80000-13 that again allows expressing it in both
|
||||
Angular quantities are not the only ones with such a "strange" behavior. Another but a similar case
|
||||
is a _storage capacity_ quantity specified in IEC-80000-13 that again allows expressing it in both
|
||||
`one` and `bit` units.
|
||||
|
||||
Those cases make dimensionless quantities an exceptional tree in the library. This is the only
|
||||
@ -245,4 +245,4 @@ inline constexpr struct steradian : named_unit<"sr", square(metre) / square(metr
|
||||
inline constexpr struct bit : named_unit<"bit", one, kind_of<storage_capacity>> {} bit;
|
||||
```
|
||||
|
||||
but still allow a usage of `one` and its scaled versions for such quantities.
|
||||
but still allow the usage of `one` and its scaled versions for such quantities.
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Faster-than-lightspeed Constants
|
||||
|
||||
In most libraries, physical constants are implemented as constant (possibly `constexpr`)
|
||||
quantity values. Such an approach has some disadvantages, often resulting in longer
|
||||
compilation times and a loss of precision.
|
||||
quantity values. Such an approach has some disadvantages, often affecting the run time
|
||||
performance and causing a loss of precision.
|
||||
|
||||
|
||||
## Simplifying constants in an equation
|
||||
@ -16,7 +16,7 @@ performance and often a better precision of the resulting value.
|
||||
|
||||
## Physical constants as units
|
||||
|
||||
The **mp-units** library allows and encourages implementing physical constants as
|
||||
The **mp-units** library allows and encourages the implementation of physical constants as
|
||||
regular units. With that, the constant's value is handled at compile-time, and under
|
||||
favorable circumstances, it can be simplified in the same way as all other repeated
|
||||
units do. If it is not simplified, the value is stored in a type, and the expensive
|
||||
@ -53,7 +53,7 @@ inline constexpr struct magnetic_constant :
|
||||
|
||||
## Usage examples
|
||||
|
||||
With the above definitions, we can calculate vacuum permittivity as:
|
||||
With the above definitions, we can calculate _vacuum permittivity_ as:
|
||||
|
||||
```cpp
|
||||
constexpr auto permeability_of_vacuum = 1. * si::magnetic_constant;
|
||||
@ -72,9 +72,9 @@ permittivity of vacuum = 1 μ₀⁻¹ c⁻² = 8.85419e-12 F/m
|
||||
|
||||
As we can clearly see, all the calculations above were just about multiplying and dividing
|
||||
the number `1` with the rest of the information provided as a compile-time type. Only when
|
||||
a user wants a specific SI unit as a result the unit ratios are lazily resolved.
|
||||
a user wants a specific SI unit as a result, the unit ratios are lazily resolved.
|
||||
|
||||
Another similar example can be an equation for total energy:
|
||||
Another similar example can be an equation for _total energy_:
|
||||
|
||||
```cpp
|
||||
QuantityOf<isq::mechanical_energy> auto total_energy(QuantityOf<isq::momentum> auto p,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Generic Interfaces
|
||||
|
||||
Using a concrete unit in the interface often has a lot of sense. It is especially useful if we
|
||||
Using a concrete unit in the interface often makes a lot of sense. It is especially useful if we
|
||||
store the data internally in the object. In such a case, we have to select a specific unit anyway.
|
||||
|
||||
For example, let's consider a simple storage tank:
|
||||
@ -30,8 +30,7 @@ However, in many cases, using a specific unit in the interface is counterproduct
|
||||
the following function:
|
||||
|
||||
```cpp
|
||||
quantity<isq::speed[km / h]> avg_speed(quantity<isq::length[km]> distance,
|
||||
quantity<isq::time[h]> duration)
|
||||
quantity<km / h> avg_speed(quantity<km> distance, quantity<h> duration)
|
||||
{
|
||||
return distance / duration;
|
||||
}
|
||||
@ -40,28 +39,28 @@ quantity<isq::speed[km / h]> avg_speed(quantity<isq::length[km]> distance,
|
||||
Everything seems fine for now. It also works great if we call it with:
|
||||
|
||||
```cpp
|
||||
quantity<isq::speed[km / h]> s1 = avg_speed(220 * km, 2 * h);
|
||||
quantity<km / h> s1 = avg_speed(220 * km, 2 * h);
|
||||
```
|
||||
|
||||
However, if the user starts doing the following:
|
||||
|
||||
```cpp
|
||||
quantity<isq::speed[mi / h]> s2 = avg_speed(140 * mi, 2 * h);
|
||||
quantity<isq::speed[m / s]> s3 = avg_speed(20 * m, 2 * s);
|
||||
quantity<mi / h> s2 = avg_speed(140 * mi, 2 * h);
|
||||
quantity<m / s> s3 = avg_speed(20 * m, 2 * s);
|
||||
```
|
||||
|
||||
some issues start to be clearly visible:
|
||||
|
||||
1. The arguments must be converted to units mandated by the function's parameters at each call.
|
||||
This involves potentially expensive multiplication/division operations at runtime.
|
||||
2. After the function returns the speed in a unit of `km/h`, another potentially expensive
|
||||
multiplication/division operations have to be performed to convert the resulting quantity into
|
||||
2. After the function returns the _speed_ in a unit of `km/h`, another potentially expensive
|
||||
multiplication/division operations must be performed to convert the resulting quantity into
|
||||
a unit being the derived unit of the initial function's arguments.
|
||||
3. Besides the obvious runtime cost, some unit conversions may result in a data truncation which
|
||||
3. Besides the obvious runtime cost, some unit conversions may result in a value truncation, which
|
||||
means that the result will not be exactly equal to a direct division of the function's arguments.
|
||||
4. We have to use a floating-point representation type (the `quantity` class template by default uses
|
||||
`double` as a representation type) which is considered
|
||||
[value preserving](value_conversions.md#value-preserving-conversions).
|
||||
[value-preserving](value_conversions.md#value-preserving-conversions).
|
||||
Trying to use an integral type in this scenario will work only for `s1`, while `s2` and `s3`
|
||||
will fail to compile. Failing to compile is a good thing here as the library tries to prevent
|
||||
the user from doing a clearly wrong thing. To make the code compile, the user needs to use
|
||||
@ -72,7 +71,7 @@ some issues start to be clearly visible:
|
||||
quantity<isq::speed[m / s]> s3 = avg_speed((20 * m).force_in(km), (2 * s).force_in(h));
|
||||
```
|
||||
|
||||
but the above will obviously provide an incorrect behavior (e.g. division by `0` in the evaluation
|
||||
but the above will obviously provide an incorrect behavior (e.g., division by `0` in the evaluation
|
||||
of `s3`).
|
||||
|
||||
|
||||
@ -87,15 +86,15 @@ auto avg_speed(auto distance, auto duration)
|
||||
}
|
||||
```
|
||||
|
||||
Beware that there are better solutions than this. The above code is too generic. Such a function template
|
||||
Beware, this is not a good solution. The above code is too generic. Such a function template
|
||||
accepts everything:
|
||||
|
||||
- quantities of other types
|
||||
- the compiler will not prevent accidental reordering of the function's arguments
|
||||
- quantities of different types can be passed as well
|
||||
- plain `double` arguments
|
||||
- the compiler will not prevent accidental reordering of the function's arguments,
|
||||
- quantities of different types can be passed as well,
|
||||
- plain `double` arguments,
|
||||
- `std::vector` and `std::lock_guard` will be accepted as well (of course, this will fail in the
|
||||
function's body later in the compilation process)
|
||||
instantiation of a function's body later in the compilation process).
|
||||
|
||||
|
||||
!!! note
|
||||
@ -110,16 +109,39 @@ accepts everything:
|
||||
Much better generic code can be implemented using [basic concepts](concepts.md)
|
||||
provided with the library:
|
||||
|
||||
```cpp
|
||||
auto avg_speed(QuantityOf<isq::length> auto distance,
|
||||
QuantityOf<isq::time> auto duration)
|
||||
{
|
||||
return isq::speed(distance / duration);
|
||||
}
|
||||
```
|
||||
=== "Original template notation"
|
||||
|
||||
```cpp
|
||||
template<typename Distance, typename Duration>
|
||||
requires QuantityOf<Distance, isq::length> && QuantityOf<Duration, isq::time>
|
||||
auto avg_speed(Distance distance, Duration duration)
|
||||
{
|
||||
return isq::speed(distance / duration);
|
||||
}
|
||||
```
|
||||
|
||||
=== "The shorthand notation"
|
||||
|
||||
```cpp
|
||||
template<QuantityOf<isq::length> Distance, QuantityOf<isq::time> Duration>
|
||||
auto avg_speed(Distance distance, Duration duration)
|
||||
{
|
||||
return isq::speed(distance / duration);
|
||||
}
|
||||
```
|
||||
|
||||
=== "Terse notation"
|
||||
|
||||
```cpp
|
||||
auto avg_speed(QuantityOf<isq::length> auto distance,
|
||||
QuantityOf<isq::time> auto duration)
|
||||
{
|
||||
return isq::speed(distance / duration);
|
||||
}
|
||||
```
|
||||
|
||||
This explicitly states that the arguments passed by the user must not only satisfy
|
||||
a [`Quantity`](concepts.md#Quantity) concept but also their quantity specification must
|
||||
a [`Quantity`](concepts.md#Quantity) concept, but also their quantity specification must
|
||||
be implicitly convertible to `isq::length` and `isq::time` accordingly. This no longer leaves
|
||||
room for error while still allowing the compiler to generate the most efficient code.
|
||||
|
||||
@ -127,7 +149,7 @@ room for error while still allowing the compiler to generate the most efficient
|
||||
|
||||
Please note that now it is safe just to use integral types all the way which again
|
||||
improves the runtime performance as the multiplication/division operations are often
|
||||
faster on integral rather than floating-point types.
|
||||
faster on the integral rather than floating-point types.
|
||||
|
||||
|
||||
## Constraining function template return type
|
||||
@ -156,7 +178,7 @@ Doing so has two important benefits:
|
||||
|
||||
## Constraining a variable on the stack
|
||||
|
||||
If we know exactly what the function does in its internals and if we know the exact argument types
|
||||
If we know precisely what the function does in its internals and if we know the exact argument types
|
||||
passed to such a function, we often know the exact type that will be returned from its invocation.
|
||||
|
||||
However, if we care about performance, we should often use the generic interfaces described in this
|
||||
@ -171,7 +193,17 @@ auto s2 = avg_speed(140 * mi, 2 * h);
|
||||
auto s3 = avg_speed(20 * m, 2 * s);
|
||||
```
|
||||
|
||||
In this case, it is probably OK to do so as the `avg_speed` function name explicitly provides
|
||||
or benefit from CTAD:
|
||||
|
||||
```cpp
|
||||
quantity s1 = avg_speed(220 * km, 2 * h);
|
||||
quantity s2 = avg_speed(140 * mi, 2 * h);
|
||||
quantity s3 = avg_speed(20 * m, 2 * s);
|
||||
```
|
||||
|
||||
*[CTAD]: Class Template Argument Deduction
|
||||
|
||||
In both cases, it is probably OK to do so as the `avg_speed` function name explicitly provides
|
||||
the information on what to expect as a result.
|
||||
|
||||
In other scenarios where the returned quantity type is not so obvious, it is again helpful to
|
||||
@ -183,7 +215,7 @@ QuantityOf<isq::speed> auto s2 = avg_speed(140 * mi, 2 * h);
|
||||
QuantityOf<isq::speed> auto s3 = avg_speed(20 * m, 2 * s);
|
||||
```
|
||||
|
||||
Again this explicitly provides additional information about the quantity we are dealing with in
|
||||
The above explicitly provides additional information about the quantity we are dealing with in
|
||||
the code, and it serves as a unit test checking if the "thing" returned from a function is actually
|
||||
what we expected here.
|
||||
|
||||
@ -192,4 +224,4 @@ what we expected here.
|
||||
|
||||
The `QuantityOf` and `QuantityPointOf` concepts are probably the most useful, but there
|
||||
are a few more to play with. A list of all the concepts can be found in
|
||||
[the "Basic Concepts" chapter](concepts.md).
|
||||
the [Basic Concepts](concepts.md) chapter.
|
||||
|
@ -8,8 +8,8 @@ properly constrained set of arithmetic operations on one or two operands.
|
||||
!!! important "Important: `quantity` propagates the underlying interface"
|
||||
|
||||
Every single arithmetic operator is exposed by the `quantity` class template only if
|
||||
the underlying representation type provides it as well and its implementation has proper
|
||||
semantics (e.g. returns a reasonable type).
|
||||
the underlying representation type provides it as well, and when its implementation has proper
|
||||
semantics (e.g., returns a reasonable type).
|
||||
|
||||
For example, in the following code, `-a` will compile only if `MyInt` exposes such an operation
|
||||
as well:
|
||||
@ -22,10 +22,10 @@ quantity b = -a;
|
||||
Assuming that:
|
||||
|
||||
- `q` is our quantity,
|
||||
- `qq` is a quantity implicitly convertible to `q`,
|
||||
- `q2` is any other quantity,
|
||||
- `kind` is a [quantity of the same kind](systems_of_quantities.md#quantities-of-the-same-kind) as `q`,
|
||||
- `one` is a [quantity of `dimension_one` with the unit `one`](dimensionless_quantities.md),
|
||||
- `qi` is a quantity implicitly convertible to `q`,
|
||||
- `qk` is a [quantity of the same kind](systems_of_quantities.md#quantities-of-the-same-kind) as `q`,
|
||||
- `q1` is a [quantity of `dimension_one` with the unit `one`](dimensionless_quantities.md),
|
||||
- `qq` is any other quantity,
|
||||
- `number` is a value of a type "compatible" with `q`'s representation type,
|
||||
|
||||
here is the list of all the supported operators:
|
||||
@ -38,26 +38,26 @@ here is the list of all the supported operators:
|
||||
- `--q`
|
||||
- `q--`
|
||||
- compound assignment:
|
||||
- `q += qq`
|
||||
- `q -= qq`
|
||||
- `q %= qq`
|
||||
- `q += qi`
|
||||
- `q -= qi`
|
||||
- `q %= qi`
|
||||
- `q *= number`
|
||||
- `q *= one`
|
||||
- `q *= q1`
|
||||
- `q /= number`
|
||||
- `q /= one`
|
||||
- `q /= q1`
|
||||
- binary:
|
||||
- `q + kind`
|
||||
- `q - kind`
|
||||
- `q % kind`
|
||||
- `q * q2`
|
||||
- `q + qk`
|
||||
- `q - qk`
|
||||
- `q % qk`
|
||||
- `q * qq`
|
||||
- `q * number`
|
||||
- `number * q`
|
||||
- `q / q2`
|
||||
- `q / qq`
|
||||
- `q / number`
|
||||
- `number / q`
|
||||
- ordering and comparison:
|
||||
- `q == kind`
|
||||
- `q <=> kind`
|
||||
- `q == qk`
|
||||
- `q <=> qk`
|
||||
|
||||
As we can see, there are plenty of operations one can do on a value of a `quantity` type. As most
|
||||
of them are obvious, in the following chapters, we will discuss only the most important or non-trivial
|
||||
@ -106,7 +106,7 @@ static_assert(isq::radius(1 * m) - 0.5 * m == isq::radius(0.5 * m));
|
||||
static_assert((isq::height(1 * m) += isq::length(1 * m)) == 2 * m); // Compile-time error(3)
|
||||
```
|
||||
|
||||
1. Floating-point to integral representation type is [considered narrowing](value_conversions.md).
|
||||
1. The floating-point to integral representation type is [considered narrowing](value_conversions.md).
|
||||
2. Conversion of quantity with integral representation type from a unit of a higher resolution to the one
|
||||
with a lower resolution is [considered narrowing](value_conversions.md).
|
||||
3. Conversion from a more generic quantity type to a more specific one is
|
||||
@ -130,9 +130,9 @@ static_assert(isq::height(3 * m) * 0.5 == isq::height(1.5 * m));
|
||||
static_assert((isq::height(3 * m) *= 0.5) == isq::height(1.5 * m)); // Compile-time error(1)
|
||||
```
|
||||
|
||||
1. Floating-point to integral representation type is [considered narrowing](value_conversions.md).
|
||||
1. The floating-point to integral representation type is [considered narrowing](value_conversions.md).
|
||||
|
||||
However, suppose we multiply or divide quantities of the same or different types, or we divide a raw
|
||||
However, suppose we multiply or divide quantities of the same or different types or we divide a raw
|
||||
number by a quantity. In that case, we most probably will end up in a quantity of yet another type:
|
||||
|
||||
```cpp
|
||||
@ -169,7 +169,7 @@ of different quantity types, we do not convert quantity values to a common unit
|
||||
!!! important "Important: Beware of integral division"
|
||||
|
||||
The physical units library can't do any runtime branching logic for the division operator.
|
||||
All logic has to be done at compile-time when the actual values are not known, and the quantity types
|
||||
All logic must be done at compile-time when the actual values are unknown, and the quantity types
|
||||
can't change at runtime.
|
||||
|
||||
If we expect `120 * km / (2 * h)` to return `60 km / h`, we have to agree with the fact that
|
||||
@ -183,7 +183,7 @@ of different quantity types, we do not convert quantity values to a common unit
|
||||
|
||||
## Modulo
|
||||
|
||||
Now that we know how addition, subtraction, multiplication, and division work, it is time to talk about
|
||||
Now that we know how addition, subtraction, multiplication, and division work, it is time to discuss
|
||||
modulo. What would we expect to be returned from the following quantity equation?
|
||||
|
||||
```cpp
|
||||
@ -278,7 +278,7 @@ static_assert(1 * h % (59 * min) == 1 * min);
|
||||
## Comparison against zero
|
||||
|
||||
In our code, we often want to compare the value of a quantity against zero. For example, we do it
|
||||
every time when we want to ensure that we deal with a non-zero or positive value.
|
||||
every time we want to ensure that we deal with a non-zero or positive value.
|
||||
|
||||
We could implement such checks in the following way:
|
||||
|
||||
@ -287,7 +287,7 @@ if (q1 / q2 != 0 * m / s)
|
||||
// ...
|
||||
```
|
||||
|
||||
The above would work (assuming we are dealing with the quantity of speed), but could be suboptimal
|
||||
The above would work (assuming we are dealing with the quantity of speed) but could be suboptimal
|
||||
if the result of `q1 / q2` is not expressed in `m / s`. To eliminate the need for conversion, we
|
||||
need to write:
|
||||
|
||||
@ -323,16 +323,15 @@ if (is_neq_zero(q1 / q2))
|
||||
|
||||
Those functions will work with any type `T` that exposes `zero()` member function returning
|
||||
something comparable to `T`. Thanks to that, we can use them not only with quantities but also
|
||||
with [quantity points](the_affine_space.md#quantity_point),
|
||||
[`std::chrono::duration`](https://en.cppreference.com/w/cpp/chrono/duration) or any other type
|
||||
that exposes such an interface.
|
||||
with [`std::chrono::duration`](https://en.cppreference.com/w/cpp/chrono/duration) or any other
|
||||
type that exposes such an interface.
|
||||
|
||||
|
||||
## Other maths
|
||||
|
||||
This chapter scopes only on the `quantity` type's operators. However, there are many named math
|
||||
functions provided in the _mp-units/math.h_ header file. Among others, we can find there
|
||||
the following:
|
||||
functions taking quantities as arguments. Those can be found in the _mp-units/math.h_ header file.
|
||||
Among others, we can find there the following:
|
||||
|
||||
- `pow()`, `sqrt()`, `cbrt()`,
|
||||
- `exp()`,
|
||||
@ -347,4 +346,4 @@ the following:
|
||||
- `asin()`, `acos()`, `atan()`.
|
||||
|
||||
In the library, we can also find _mp-units/random.h_ header file with all the pseudo-random number
|
||||
generators.
|
||||
generators working on quantity types.
|
||||
|
@ -139,7 +139,7 @@ error: could not convert 'mp_units::operator*<si::metre(), double, si::second(),
|
||||
|
||||
Simple mode is all about and just about units. In case we care about a specific quantity type,
|
||||
**typed quantities** should be preferred. With this mode, for example, we can specify if we
|
||||
deal with `width`, `height`, or `radius` and ensure we will not assign one to another by
|
||||
deal with _width_, _height_, or _radius_ and ensure we will not assign one to another by
|
||||
accident.
|
||||
|
||||
The previous example can be re-typed using typed quantities in the following way:
|
||||
@ -178,7 +178,7 @@ A car driving 110 km in 2 h has an average speed of 15.2778 m/s (55 km/h)
|
||||
!!! example "[Try it on Compiler Explorer](https://godbolt.org/z/q3PzMzqsh)"
|
||||
|
||||
In case we will accidentally make the same calculation error as before, this time, we will
|
||||
get a bit longer error message also containing information about the quantity type:
|
||||
get a bit longer error message, this time also containing information about the quantity type:
|
||||
|
||||
```log
|
||||
In function 'constexpr mp_units::quantity<mp_units::reference<mp_units::isq::speed(), mp_units::derived_unit<mp_units::si::metre, mp_units::per<mp_units::si::second> >()>()> avg_speed(mp_units::quantity<mp_units::reference<mp_units::isq::length(), mp_units::si::metre()>()>, mp_units::quantity<mp_units::reference<mp_units::isq::time(), mp_units::si::second()>()>)':
|
||||
@ -196,7 +196,7 @@ As we can see above, the compilation error is longer but still relatively easy t
|
||||
|
||||
Based on the previous example, it might seem that typed quantities are not that useful,
|
||||
more to type and provide harder-to-understand error messages. It might be true in some cases,
|
||||
but there are scenarios where they offer an additional level of safety.
|
||||
but there are scenarios where they offer additional level of safety.
|
||||
|
||||
Let's see another example:
|
||||
|
||||
@ -414,9 +414,9 @@ quantities-related issues, this should be the first function to look for.
|
||||
requested conversion is exactly what you need in this case.
|
||||
|
||||
|
||||
## Which mode to use in my project?
|
||||
## Which mode should I use in my project?
|
||||
|
||||
In case you wonder which mode you should choose for your project, we have good news for you.
|
||||
We have good news for you if you wonder which mode you should choose for your project.
|
||||
Simple and typed quantity modes can be freely mixed with each other. When you use different
|
||||
quantities of the same kind (e.g., _radius_, _wavelength_, _altitude_, ...), you should probably
|
||||
reach for typed quantities to bring additional safety for those cases. Otherwise, just use
|
||||
|
@ -8,9 +8,9 @@ std::cout << q1.in(m) << '\n';
|
||||
quantity<si::metre, int> q2 = q1;
|
||||
```
|
||||
|
||||
The second line above converts the current quantity to the one expressed in metres and prints its
|
||||
contents. The third line converts the quantity expressed in kilometres into the one measured
|
||||
in metres.
|
||||
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.
|
||||
|
||||
In case a user would like to perform an opposite transformation:
|
||||
|
||||
@ -65,7 +65,7 @@ reviews or while chasing a bug in the source code.
|
||||
`q.force_numerical_value_in(U)`.
|
||||
|
||||
Another place where this cast is useful is when a user wants to convert a quantity with
|
||||
a floating-point representation to the one using an integral one. Again this is a truncating
|
||||
a floating-point representation to the one using an integral one. Again, this is a truncating
|
||||
conversion, so an explicit cast is needed:
|
||||
|
||||
```cpp
|
||||
@ -74,7 +74,7 @@ quantity<si::metre, int> q3 = value_cast<int>(3.14 * m);
|
||||
|
||||
!!! info
|
||||
|
||||
It is often fine to use an integral as a representation type, but in general, floating-point
|
||||
It is often OK 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.
|
||||
|
||||
|
Reference in New Issue
Block a user