docs: "Framework Basics" chapters updated and cleaned up

This commit is contained in:
Mateusz Pusz
2023-12-26 11:07:21 +01:00
parent 1db975d48c
commit 1b5b4dbdcd
8 changed files with 160 additions and 128 deletions

View File

@ -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" }

View File

@ -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

View File

@ -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.

View File

@ -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,

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.