Merge branch 'master' of github.com:mpusz/units

This commit is contained in:
Mateusz Pusz
2023-11-08 15:37:16 -10:00
7 changed files with 122 additions and 85 deletions

View File

@@ -333,6 +333,20 @@
associated with a specific quantity ([quantity specification](#quantity_spec) and associated with a specific quantity ([quantity specification](#quantity_spec) and
[unit](#unit)). [unit](#unit)).
[`canonical representation of a unit, canonical unit`](#canonical-unit){ #canonical-unit }
: - A canonical representation of a unit consists of:
- a reference unit being the result of extraction of all the intermediate
[derived units](#derived-unit),
- a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
- All units having the same canonical unit are deemed equal.
- All units having the same reference unit are convertible
(their magnitude may differ and is used during conversion).
[`reference unit`](#reference-unit){ #reference-unit }
: See [canonical representation of a unit](#canonical-unit)
[`absolute quantity point origin`, `absolute point origin`](#absolute-point-origin){ #absolute-point-origin } [`absolute quantity point origin`, `absolute point origin`](#absolute-point-origin){ #absolute-point-origin }
: - An explicit point on an axis of values of a specific [quantity](#quantity) type that serves : - An explicit point on an axis of values of a specific [quantity](#quantity) type that serves

View File

@@ -19,7 +19,7 @@ flowchart TD
quantity_character["Quantity character"] --- QuantitySpec quantity_character["Quantity character"] --- QuantitySpec
QuantitySpec --- Reference["Quantity reference"] QuantitySpec --- Reference["Quantity reference"]
Reference --- Quantity Reference --- Quantity
quantity_character --- Representation quantity_character -.- Representation
Representation --- Quantity Representation --- Quantity
Quantity --- QuantityPoint["Quantity point"] Quantity --- QuantityPoint["Quantity point"]
PointOrigin["Point origin"] --- QuantityPoint PointOrigin["Point origin"] --- QuantityPoint

View File

@@ -1,16 +1,18 @@
# Simple and Typed Quantities # Simple and Typed Quantities
ISO specifies a quantity as: ISO defines a quantity as:
!!! quote !!! quote
property of a phenomenon, body, or substance, where the property has a magnitude that can be expressed as a number and a reference property of a phenomenon, body, or substance, where the property has a magnitude
that can be expressed as a number and a reference
After that, it says: After that, it says:
!!! quote !!! quote
A reference can be a measurement unit, a measurement procedure, a reference material, or a combination of such. A reference can be a measurement unit, a measurement procedure, a reference material,
or a combination of such.
## `quantity` class template ## `quantity` class template
@@ -22,9 +24,10 @@ template<Reference auto R,
class quantity; class quantity;
``` ```
The concept `Reference` is satisfied by either: The concept `Reference` is satisfied by a type that provides all the domain-specific metadata describing
a quantity (besides the representation type and its value). Such a type can be either:
- a unit with an associated quantity type (e.g. `si::metre`) - a unit with an associated quantity type (e.g., `si::metre`, `m / s`),
- a reference type explicitly specifying the quantity type and its unit. - a reference type explicitly specifying the quantity type and its unit.
!!! important !!! important
@@ -37,9 +40,10 @@ A reference type is implicitly created as a result of the following expression:
constexpr auto ref = isq::length[m]; constexpr auto ref = isq::length[m];
``` ```
The above example resulted in the following type `reference<isq::length(), si::metre()>` being instantiated. The above example results in the following type `reference<isq::length(), si::metre()>` being instantiated.
Based on this property, the **mp-units** library provides two modes of dealing with quantities. As we have two alternative options that satisfy the `Reference` concept in the **mp-units** library,
we also have two modes of dealing with quantities.
## Simple quantities ## Simple quantities
@@ -85,7 +89,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/zWe8ecf93)" !!! example "[Try it on Compiler Explorer](https://godbolt.org/z/zWe8ecf93)"
### Easy to understand compilation error messages ### Easy-to-understand compilation error messages
In case a user makes an error in a quantity equation and the result of the calculation In case a user makes an error in a quantity equation and the result of the calculation
will not match the function return type, the compiler will detect such an issue at will not match the function return type, the compiler will detect such an issue at
@@ -175,7 +179,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, 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, more to type and provide harder-to-understand error messages. It might be true in some cases,
but there are cases where they provide an additional level of safety. but there are scenarios where they offer an additional level of safety.
Let's see another example: Let's see another example:
@@ -292,7 +296,7 @@ Let's see another example:
In the above example, the highlighted call doesn't look that safe anymore in the case In the above example, the highlighted call doesn't look that safe anymore in the case
of simple quantities, right? Suppose someone, either by mistake or due to some refactoring, of simple quantities, right? Suppose someone, either by mistake or due to some refactoring,
will call the function with invalid order of arguments. In that case, the program will compile will call the function with an invalid order of arguments. In that case, the program will compile
fine but not work as expected. fine but not work as expected.
Let's see what will happen if we reorder the arguments in the case of typed quantities: Let's see what will happen if we reorder the arguments in the case of typed quantities:
@@ -303,7 +307,7 @@ auto tank = RectangularStorageTank(horizontal_length(1'000 * mm),
isq::width(500 * mm)); isq::width(500 * mm));
``` ```
This time a compiler provides the following compilation error: This time, a compiler provides the following compilation error:
```text ```text
In function 'int main()': In function 'int main()':
@@ -319,7 +323,7 @@ note: no known conversion for argument 2 from 'mp_units::quantity<mp_units::re
``` ```
What about derived quantities? In the above example, you probably noticed that we also defined What about derived quantities? In the above example, you probably noticed that we also defined
a custom `horizontal_area` quantity of kind `isq::area`. This quantity has the special property a custom `horizontal_area` quantity of kind `isq::area`. This quantity has the unique property
of being implicitly constructible only from the result of the multiplication of quantities of of being implicitly constructible only from the result of the multiplication of quantities of
`horizontal_area` and `isq::width` or the ones that implicitly convert to them. `horizontal_area` and `isq::width` or the ones that implicitly convert to them.
@@ -377,15 +381,15 @@ public:
}; };
``` ```
As `isq::radius` is not convertible to either a `horizontal_length` or `isq::width`, As `isq::radius` is not convertible to `horizontal_length`, the derived quantity of
the derived quantity of `pow<2>(radius)` can't be converted to `horizontal_area` as well. `pow<2>(radius)` can't be converted to `horizontal_area` as well.
It would be unsafe to allow such a conversion as not all of the circles lie flat on the It would be unsafe to allow such a conversion as not all of the circles lie flat on the
ground, right? ground, right?
In such a case, the user has to explicitly force such an unsafe conversion with the In such a case, the user has to explicitly force such an unsafe conversion with the
help of a `quantity_cast()`. This function name is easy to spot in code reviews or while help of a `quantity_cast()`. This function name is easy to spot in code reviews or while
searching the project for problems if something goes sideways. In case of unexpected issues searching the project for problems if something goes sideways. In case of unexpected
related to quantities, this should be the first function to look for. quantities-related issues, this should be the first function to look for.
!!! tip !!! tip
@@ -397,7 +401,7 @@ related to quantities, this should be the first function to look for.
In case you wonder which mode you should choose for your project, we have good news for you. In case you wonder which mode you should choose for your project, we have good news for you.
Simple and typed quantity modes can be freely mixed with each other. When you use different 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 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 reach for typed quantities to bring additional safety for those cases. Otherwise, just use
simple mode for the remaining quantities. The **mp-units** library will do its best to protect simple mode for the remaining quantities. The **mp-units** library will do its best to protect
your project based on the information provided. your project based on the information provided.

View File

@@ -33,10 +33,10 @@ Box my_box(2 * m, 3 * m, 1 * m);
How do you like such an interface? It turns out that in most existing strongly-typed libraries How do you like such an interface? It turns out that in most existing strongly-typed libraries
this is often the best we can do :woozy_face: this is often the best we can do :woozy_face:
Another typical question many users ask is how to deal with energy and moment of force. Another typical question many users ask is how to deal with _work_ and _torque_.
Both of those have the same dimension but are different quantities. Both of those have the same dimension but are different quantities.
Another question is what should be the result of: A similar issue is related to figuring out what should be the result of:
```cpp ```cpp
auto res = 1 * Hz + 1 * Bq + 1 * Bd; auto res = 1 * Hz + 1 * Bq + 1 * Bd;
@@ -44,20 +44,27 @@ auto res = 1 * Hz + 1 * Bq + 1 * Bd;
where: where:
- `Hz` (hertz) - unit of frequency - `Hz` (hertz) - unit of _frequency_
- `Bq` (becquerel) - unit of activity - `Bq` (becquerel) - unit of _activity_
- `Bd` (baud) - unit of modulation rate - `Bd` (baud) - unit of _modulation rate_
All of those quantities have the same dimension, namely $\mathsf{T}^{-1}$, but probably it All of those quantities have the same dimension, namely $\mathsf{T}^{-1}$, but probably it
is not wise to allow adding, subtracting, or comparing them, as they describe vastly different is not wise to allow adding, subtracting, or comparing them, as they describe vastly different
physical properties. physical properties.
If the above example seems too abstract, let's consider a _fuel consumption_ (fuel _volume_
divided by _distance_, e.g., `6.7 l/km`) and an _area_. Again, both have the same dimension
$\mathsf{L}^{2}$, but probably it wouldn't be wise to allow adding, subtracting, or comparing
a _fuel consumption_ of a car and the _area_ of a football field. Such an operation does not
have any physical sense and should fail to compile.
!!! important !!! important
More than one quantity may be defined for the same dimension: More than one quantity may be defined for the same dimension:
- quantities of _different kinds_ (e.g. frequency, modulation rate, activity, ...) - quantities of **different kinds** (e.g. _frequency_, _modulation rate_, _activity_, ...)
- quantities of _the same kind_ (e.g. length, width, altitude, distance, radius, wavelength, position vector, ...) - quantities of **the same kind** (e.g. _length_, _width_, _altitude_, _distance_, _radius_,
_wavelength_, _position vector_, ...)
It turns out that the above issues can't be solved correctly without proper modeling of It turns out that the above issues can't be solved correctly without proper modeling of
a [system of quantities](../../appendix/glossary.md#system-of-quantities). a [system of quantities](../../appendix/glossary.md#system-of-quantities).
@@ -76,7 +83,7 @@ a [system of quantities](../../appendix/glossary.md#system-of-quantities).
dimension** dimension**
- Quantities of the **same dimension are not necessarily of the same kind** - Quantities of the **same dimension are not necessarily of the same kind**
The above quotes from ISO 80000 answer to all the issues above. Two quantities can't be The above quotes from ISO 80000 provide answers to all the issues above. Two quantities can't be
added, subtracted, or compared unless they belong to the same [kind](../../appendix/glossary.md#kind). added, subtracted, or compared unless they belong to the same [kind](../../appendix/glossary.md#kind).
As frequency, activity, and modulation rate are different kinds, the expression provided above should As frequency, activity, and modulation rate are different kinds, the expression provided above should
not compile. not compile.
@@ -106,11 +113,12 @@ flowchart TD
radius --- radius_of_curvature radius --- radius_of_curvature
``` ```
Each of the above quantities expresses some kind of length, and each can be measured with `si::metre`. Each of the above quantities expresses some kind of _length_, and each can be measured with `si::metre`.
However, each of them has different properties, usage, and sometimes even a different However, each of them has different properties, usage, and sometimes even requires a different
representation type (notice that `position_vector` and `displacement` are vector quantities). representation type (notice that `position_vector` and `displacement` are vector quantities).
Analyzing such a hierarchy can help us in defining arithmetics and conversion rules. Such a hierarchy helps us in defining arithmetics and conversion rules for various quantities of
the same kind.
## Defining quantities ## Defining quantities
@@ -206,14 +214,14 @@ For example, here is how the above quantity kind tree can be modeled in the libr
## Comparing, adding, and subtracting quantities ## Comparing, adding, and subtracting quantities
ISO 80000 explicitly states that `width` and `height` are quantities of the same kind, and as such they: ISO 80000 explicitly states that _width_ and _height_ are quantities of the same kind, and as such they:
- are mutually comparable - are mutually comparable,
- can be added and subtracted - can be added and subtracted.
If we take the above for granted, the only reasonable result of `1 * width + 1 * height` is `2 * length`, If we take the above for granted, the only reasonable result of `1 * width + 1 * height` is `2 * length`,
where the result of `length` is known as a common quantity type. A result of such an equation is always where the result of `length` is known as a **common quantity** type. A result of such an equation is always
the first common branch in a hierarchy tree of the same kind. For example: the first common node in a hierarchy tree of the same kind. For example:
```cpp ```cpp
static_assert(common_quantity_spec(isq::width, isq::height) == isq::length); static_assert(common_quantity_spec(isq::width, isq::height) == isq::length);
@@ -228,33 +236,33 @@ Based on the same hierarchy of quantities of kind length, we can define quantity
1. **Implicit conversions** 1. **Implicit conversions**
- every `width` is a `length` - every _width_ is a _length_
- every `radius` is a `width` - every _radius_ is a _width_
```cpp ```cpp
static_assert(implicitly_convertible(isq::width, isq::length)); static_assert(implicitly_convertible(isq::width, isq::length));
static_assert(implicitly_convertible(isq::radius, isq::length));
static_assert(implicitly_convertible(isq::radius, isq::width)); static_assert(implicitly_convertible(isq::radius, isq::width));
static_assert(implicitly_convertible(isq::radius, isq::length));
``` ```
2. **Explicit conversions** 2. **Explicit conversions**
- not every `length` is a `width` - not every _length_ is a _width_
- not every `width` is a `radius` - not every _width_ is a _radius_
```cpp ```cpp
static_assert(!implicitly_convertible(isq::length, isq::width)); static_assert(!implicitly_convertible(isq::length, isq::width));
static_assert(!implicitly_convertible(isq::length, isq::radius));
static_assert(!implicitly_convertible(isq::width, isq::radius)); static_assert(!implicitly_convertible(isq::width, isq::radius));
static_assert(!implicitly_convertible(isq::length, isq::radius));
static_assert(explicitly_convertible(isq::length, isq::width)); static_assert(explicitly_convertible(isq::length, isq::width));
static_assert(explicitly_convertible(isq::length, isq::radius));
static_assert(explicitly_convertible(isq::width, isq::radius)); static_assert(explicitly_convertible(isq::width, isq::radius));
static_assert(explicitly_convertible(isq::length, isq::radius));
``` ```
3. **Explicit casts** 3. **Explicit casts**
- `height` is not a `width` - _height_ is not a _width_
- both `height` and `width` are quantities of kind `length` - both _height_ and _width_ are quantities of kind _length_
```cpp ```cpp
static_assert(!implicitly_convertible(isq::height, isq::width)); static_assert(!implicitly_convertible(isq::height, isq::width));
@@ -264,7 +272,7 @@ Based on the same hierarchy of quantities of kind length, we can define quantity
4. **No conversion** 4. **No conversion**
- `time` has nothing in common with `length` - _time_ has nothing in common with _length_
```cpp ```cpp
static_assert(!implicitly_convertible(isq::time, isq::length)); static_assert(!implicitly_convertible(isq::time, isq::length));
@@ -286,12 +294,12 @@ The below presents some arbitrary hierarchy of derived quantities of kind energy
```mermaid ```mermaid
flowchart TD flowchart TD
energy["energy\n(mass * length^2 / time^2)"] energy["energy\n(mass * length<sup>2</sup> / time<sup>2</sup>)"]
energy --- mechanical_energy energy --- mechanical_energy
mechanical_energy --- potential_energy mechanical_energy --- potential_energy
potential_energy --- gravitational_potential_energy["gravitational_potential_energy\n(mass * acceleration_of_free_fall * height)"] potential_energy --- gravitational_potential_energy["gravitational_potential_energy\n(mass * acceleration_of_free_fall * height)"]
potential_energy --- elastic_potential_energy["elastic_potential_energy\n(spring_constant * amount_of_compression^2)"] potential_energy --- elastic_potential_energy["elastic_potential_energy\n(spring_constant * amount_of_compression<sup>2</sup>)"]
mechanical_energy --- kinetic_energy["kinetic_energy\n(mass * speed^2)"] mechanical_energy --- kinetic_energy["kinetic_energy\n(mass * speed<sup>2</sup>)"]
energy --- enthalpy energy --- enthalpy
enthalpy --- internal_energy[internal_energy, thermodynamic_energy] enthalpy --- internal_energy[internal_energy, thermodynamic_energy]
internal_energy --- Helmholtz_energy[Helmholtz_energy, Helmholtz_function] internal_energy --- Helmholtz_energy[Helmholtz_energy, Helmholtz_function]
@@ -301,21 +309,21 @@ flowchart TD
Notice, that even though all of those quantities have the same dimension and can be expressed Notice, that even though all of those quantities have the same dimension and can be expressed
in the same units, they have different [quantity equations](../../appendix/glossary.md#quantity-equation) in the same units, they have different [quantity equations](../../appendix/glossary.md#quantity-equation)
used to create them implicitly: that can be used to create them implicitly:
- `energy` is the most generic one and thus can be created from base quantities of `mass`, `length`, - _energy_ is the most generic one and thus can be created from base quantities of _mass_, _length_,
and `time`. As those are also the roots of quantities of their kinds and all other quantities are and _time_. As those are also the roots of quantities of their kinds and all other quantities from their
implicitly convertible to them (we agreed on that "every `width` is a `length`" already), it means trees are implicitly convertible to them (we agreed on that "every _width_ is a _length_" already),
that an `energy` can be implicitly constructed from any quantity of mass, length, and time. it means that an _energy_ can be implicitly constructed from any quantity of _mass_, _length_, and _time_:
```cpp ```cpp
static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy)); static_assert(implicitly_convertible(isq::mass * pow<2>(isq::length) / pow<2>(isq::time), isq::energy));
static_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy)); static_assert(implicitly_convertible(isq::mass * pow<2>(isq::height) / pow<2>(isq::time), isq::energy));
``` ```
- `mechanical_energy` is a more "specialized" quantity than `energy` (not every `energy` is - _mechanical energy_ is a more "specialized" quantity than _energy_ (not every _energy_ is
a `mechanical_energy`). It is why an explicit cast is needed to convert from either `energy` or a _mechanical energy_). It is why an explicit cast is needed to convert from either _energy_ or
the results of its [quantity equation](../../appendix/glossary.md#quantity-equation). the results of its [quantity equation](../../appendix/glossary.md#quantity-equation):
```cpp ```cpp
static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy)); static_assert(!implicitly_convertible(isq::energy, isq::mechanical_energy));
@@ -326,10 +334,10 @@ used to create them implicitly:
isq::mechanical_energy)); isq::mechanical_energy));
``` ```
- `gravitational_potential_energy` is not only even more specialized one but additionally, - _gravitational potential energy_ is not only even more specialized one but additionally,
it is special in a way that it provides its own "constrained" it is special in a way that it provides its own "constrained"
[quantity equation](../../appendix/glossary.md#quantity-equation). Maybe not every [quantity equation](../../appendix/glossary.md#quantity-equation). Maybe not every
`mass * pow<2>(length) / pow<2>(time)` is a `gravitational_potential_energy`, but every `mass * pow<2>(length) / pow<2>(time)` is a _gravitational potential energy_, but every
`mass * acceleration_of_free_fall * height` is. `mass * acceleration_of_free_fall * height` is.
```cpp ```cpp
@@ -372,7 +380,7 @@ Additionally, the result of operations on quantity kinds is also a quantity kind
static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>); static_assert(same_type<kind_of<isq::length> / kind_of<isq::time>, kind_of<isq::length / isq::time>>);
``` ```
However, if at least one equation's operand is not a kind, the result becomes a "strong" However, if at least one equation's operand is not a quantity kind, the result becomes a "strong"
quantity where all the kinds are converted to the hierarchy tree's root quantities: quantity where all the kinds are converted to the hierarchy tree's root quantities:
```cpp ```cpp

View File

@@ -7,14 +7,14 @@ quantities and provide automated conversion factors between various compatible u
Probably all the libraries in the wild model the [SI](../../appendix/glossary.md#si) Probably all the libraries in the wild model the [SI](../../appendix/glossary.md#si)
and many of them provide support for additional units belonging to various other systems and many of them provide support for additional units belonging to various other systems
(e.g. imperial). (e.g., imperial, cgs, etc).
## Systems of Units are based on Systems of Quantities ## Systems of Units are based on Systems of Quantities
[Systems of quantities](../../appendix/glossary.md#system-of-quantities) specify a set [Systems of quantities](../../appendix/glossary.md#system-of-quantities) specify a set
of quantities and equations relating to those quantities. Those equations do not take any of quantities and equations relating to those quantities. Those equations do not take any
unit or a numerical representation into account at all. In order to create a quantity, unit or a numerical representation into account at all. To create a quantity,
we need to add those missing pieces of information. This is where we need to add those missing pieces of information. This is where
a [system of units](../../appendix/glossary.md#system-of-units) kicks in. a [system of units](../../appendix/glossary.md#system-of-units) kicks in.
@@ -33,14 +33,14 @@ inline constexpr struct metre : named_unit<"m", kind_of<isq::length>> {} metre;
The `kind_of<isq::length>` above states explicitly that this unit has The `kind_of<isq::length>` above states explicitly that this unit has
an associated quantity kind. In other words, `si::metre` (and scaled units based an associated quantity kind. In other words, `si::metre` (and scaled units based
on it) can be used to express the amount of any quantity of kind length. on it) can be used to express the amount of any quantity of kind _length_.
## Units compose ## Units compose
One of the strongest points of the [SI](../../appendix/glossary.md#si) system One of the most vital points of the [SI](../../appendix/glossary.md#si) system
is that its units compose. This allows providing thousands of different units for is that its units compose. This allows providing thousands of different units for
hundreds of various quantities with a really small set of predefined units hundreds of various quantities with a tiny set of predefined units
and prefixes. and prefixes.
The same is modeled in the **mp-units** library, which also allows composing The same is modeled in the **mp-units** library, which also allows composing
@@ -51,9 +51,9 @@ predefined units to create a nearly infinite number of different
quantity<si::metre / si::second> q; quantity<si::metre / si::second> q;
``` ```
to express a quantity of speed. The resulting quantity type is implicitly inferred to express a quantity of _speed_. The resulting quantity type is implicitly inferred
from the [unit equation](../../appendix/glossary.md#unit-equation) by repeating from the [unit equation](../../appendix/glossary.md#unit-equation) by repeating
exactly the same operations on the associated quantity kinds. the same operations on the associated quantity kinds.
## Many shades of the same unit ## Many shades of the same unit
@@ -64,13 +64,13 @@ The [SI](../../appendix/glossary.md#si) provides the names for 22 common
Each such named [derived unit](../../appendix/glossary.md#derived-unit) is a result Each such named [derived unit](../../appendix/glossary.md#derived-unit) is a result
of a specific predefined [unit equation](../../appendix/glossary.md#unit-equation). of a specific predefined [unit equation](../../appendix/glossary.md#unit-equation).
For example, a unit of power quantity is defined in the library as: For example, a unit of _power_ quantity is defined in the library as:
```cpp ```cpp
inline constexpr struct watt : named_unit<"W", joule / second> {} watt; inline constexpr struct watt : named_unit<"W", joule / second> {} watt;
``` ```
However, a power quantity can be expressed in other units as well. For example, However, a _power_ quantity can be expressed in other units as well. For example,
the following: the following:
```cpp ```cpp
@@ -96,23 +96,26 @@ All of the above quantities are equivalent and mean exactly the same.
## Constraining a derived unit to work only with a specific derived quantity ## Constraining a derived unit to work only with a specific derived quantity
Some derived units are valid only for specific derived quantities. For example, Some derived units are valid only for specific derived quantities. For example,
SI specifies both `hertz` and `becquerel` derived units with the same unit equation `1 / s`. [SI](../../appendix/glossary.md#si) specifies both `hertz` and `becquerel` derived units
However, it also explicitly states: with the same unit equation `1 / s`. However, it also explicitly states:
!!! quote "SI Brochure" !!! quote "SI Brochure"
The hertz shall only be used for periodic phenomena and the becquerel shall only be used for The hertz shall only be used for periodic phenomena and the becquerel shall only be used for
stochastic processes in activity referred to a radionuclide. stochastic processes in activity referred to a radionuclide.
The library allows constraining such units in the following way: The above means that the usage of `becquerel` as a unit of a _frequency_ quantity is an error.
The library allows constraining such units to work only with quantities of a specific kind in
the following way:
```cpp ```cpp
inline constexpr struct hertz : named_unit<"Hz", one / second, kind_of<isq::frequency>> {} hertz; inline constexpr struct hertz : named_unit<"Hz", one / second, kind_of<isq::frequency>> {} hertz;
inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel; inline constexpr struct becquerel : named_unit<"Bq", one / second, kind_of<isq::activity>> {} becquerel;
``` ```
With the above, `hertz` can only be used for frequencies while becquerel should only be used for With the above, `hertz` can only be used with _frequencies_, while `becquerel` should only be used with
quantities of activity. This means that the following equation will not compile: quantities of _activity_. This means that the following equation will not compile:
```cpp ```cpp
auto q = 1 * Hz + 1 * Bq; // Fails to compile auto q = 1 * Hz + 1 * Bq; // Fails to compile
@@ -131,7 +134,7 @@ Implementation of `std::ratio` provided by all major compilers is able to expres
16 of them. This is why, in the **mp-units**, we had to find an alternative way to represent 16 of them. This is why, in the **mp-units**, we had to find an alternative way to represent
unit magnitude in a more flexible way. unit magnitude in a more flexible way.
Each prefix is implemented as: Each prefix is implemented similarly to the following:
```cpp ```cpp
template<PrefixableUnit auto U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U> {}; template<PrefixableUnit auto U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U> {};
@@ -145,7 +148,7 @@ way:
inline constexpr auto qm = quecto<metre>; inline constexpr auto qm = quecto<metre>;
``` ```
The usage of `mag_power` not only enables providing support for SI prefixes but it can also The usage of `mag_power` not only enables providing support for SI prefixes, but it can also
efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the efficiently represent any rational magnitude. For example, IEC 80000 prefixes used in the
IT industry can be implemented as: IT industry can be implemented as:
@@ -157,10 +160,10 @@ template<PrefixableUnit auto U> inline constexpr yobi_<U> yobi;
## Scaled units ## Scaled units
In the [SI](../../appendix/glossary.md#si), all units are either base or derived units or prefixed In the [SI](../../appendix/glossary.md#si), all units are either base or derived units or prefixed
versions of those. However, those are not the only options possible. versions of those. However, those are only some of the options possible.
For example, there is a list of [off-system units](../../appendix/glossary.md#off-system-unit) For example, there is a list of [off-system units](../../appendix/glossary.md#off-system-unit)
accepted for use with SI. All of those are scaled versions of the SI units with ratios that can't accepted for use with SI. Those are scaled versions of the SI units with ratios that can't
be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or be explicitly expressed with predefined SI prefixes. Those include units like minute, hour, or
electronvolt: electronvolt:

View File

@@ -41,6 +41,15 @@ inline constexpr struct great_british_pound : named_unit<"GBP", kind_of<currency
inline constexpr struct japanese_jen : named_unit<"JPY", kind_of<currency>> {} japanese_jen; inline constexpr struct japanese_jen : named_unit<"JPY", kind_of<currency>> {} japanese_jen;
// clang-format on // clang-format on
namespace unit_symbols {
inline constexpr auto EUR = euro;
inline constexpr auto USD = us_dollar;
inline constexpr auto GBP = great_british_pound;
inline constexpr auto JPY = japanese_jen;
} // namespace unit_symbols
static_assert(!std::equality_comparable_with<quantity<euro, int>, quantity<us_dollar, int>>); static_assert(!std::equality_comparable_with<quantity<euro, int>, quantity<us_dollar, int>>);
@@ -88,7 +97,9 @@ quantity_point<To, PO, Rep> exchange_to(quantity_point<From, PO, Rep> q)
int main() int main()
{ {
quantity_point price_usd = zero + 100 * us_dollar; using namespace unit_symbols;
quantity_point price_usd = zero + 100 * USD;
quantity_point price_euro = exchange_to<euro>(price_usd); quantity_point price_euro = exchange_to<euro>(price_usd);
std::cout << price_usd.quantity_from(zero) << " -> " << price_euro.quantity_from(zero) << "\n"; std::cout << price_usd.quantity_from(zero) << " -> " << price_euro.quantity_from(zero) << "\n";

View File

@@ -262,16 +262,13 @@ struct is_one<struct one> : std::true_type {};
/** /**
* @brief A canonical representation of a unit * @brief A canonical representation of a unit
* *
* A canonical representation of a unit consists of a `reference_unit` and its scaling * A canonical representation of a unit consists of:
* factor represented by the magnitude `mag`. * - a reference unit being the result of extraction of all the intermediate derived units,
* * - a magnitude being a product of all the prefixes and magnitudes of extracted scaled units.
* `reference_unit` is a unit (possibly derived one) that consists only named base units.
* All of the intermediate derived units are extracted, prefixes and magnitudes of scaled
* units are stripped from them and accounted in the `mag`.
* *
* All units having the same canonical unit are deemed equal. * All units having the same canonical unit are deemed equal.
* All units having the same `reference_unit` are convertible (their `mag` may differ * All units having the same reference unit are convertible (their magnitude may differ and
* and is the subject of conversion). * is used during conversion).
* *
* @tparam U a unit to use as a `reference_unit` * @tparam U a unit to use as a `reference_unit`
* @tparam M a Magnitude representing an absolute scaling factor of this unit * @tparam M a Magnitude representing an absolute scaling factor of this unit