docs: 📝 dimensionless quantities documentation added

This commit is contained in:
Mateusz Pusz
2020-09-08 17:13:38 +02:00
parent 8c9986dec8
commit 33ad51311b
3 changed files with 161 additions and 0 deletions

View File

@@ -28,6 +28,34 @@ different dimensions (i.e. height, width, and depth) all of them will just be
measured in meters. measured in meters.
Why a dimensionless quantity is not just an fundamental arithmetic type?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the initial design of this library the resulting type of the division of
two quantities was their common representation type::
static_assert(std::is_same_v<decltype(10q_km / 5q_km), std::int64_t>);
The reasoning behind it was to not provide a false impression of a strong `quantity` type
for something that looks and feels like a regular number. Also all of the mathematic
and trigonometric functions were working fine out of the box with such representation
types, so we did not have to rewrite ``sin()``, ``cos()``, ``exp()``, and others.
However, the feedback we got from the production usage was that such an approach
is really bad for generic programming. It is really hard to handle the result of
division (or multiplication) of two quantities as it might be either a `quantity`
or a fundamental type. If we want to raise such a result to some power we have to
either use ``units::pow`` or ``std::pow`` depending on the resulting type. Those
are only a few from many similar issues related to such an approach.
This is why it was decided to go with the current approach.
.. seealso::
More information on the current design can be found in :ref:`Dimensionless quantities`
chapter.
Why do we spell ``metre`` instead of ``meter``? Why do we spell ``metre`` instead of ``meter``?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -99,3 +99,80 @@ or a specific target `quantity_point`::
For more information on conversion and casting and on how to extend the above "integral" For more information on conversion and casting and on how to extend the above "integral"
vs "floating-point" logic please refer to the :ref:`Using Custom Representation Types` vs "floating-point" logic please refer to the :ref:`Using Custom Representation Types`
chapter. chapter.
Implicit conversions of dimensionless quantities
------------------------------------------------
As noted in the :ref:`Dimensionless Quantities` chapter, :term:`quantity of dimension one`
is somehow special but still obey most of the rules defined for quantities. However, as they
represent numbers it would be highly uncomfortable to every time type::
const auto d1 = 10q_km;
const auto d2 = 3q_km;
if(d1 / d2 > dimensionless<unitless, 2>) {
// ...
}
or::
const auto fill_time_left = (box.height / box.fill_level(measured_mass) -
dimensionless<unitless, 1>) * fill_time;
This is why it was decided to allow the ``dimensionless<unitless>`` quantity of any
representation type to be implicitly constructible from this representation type.
With that the above examples can be rewritten as follows::
const auto d1 = 10q_km;
const auto d2 = 3q_km;
if(d1 / d2 > 2) {
// ...
}
and::
const auto fill_time_left = (box.height / box.fill_level(measured_mass) - 1) * fill_time;
The above is true only for dimensionless quantities of `unitless` unit. If our quantity have a unit with
ratio different than ``1`` the implicit conversion will not happen. This is to prevent cases were the code
could be ambiguous. For example::
Dimensionless auto foo(Length auto d1, Length auto d2)
{
return d1 / d2 + 1;
}
As long as we can reason about what such code means for ``foo(10q_km, 2q_km)`` it is not that obvious
at all in the case of ``foo(10q_cm, 2q_ft)``. To make such code to compile for every case we have to
either change the type of the resulting unit to the one having ``ratio(1)`` (:term:`coherent derived unit`)::
Dimensionless auto foo(Length auto d1, Length auto d2)
{
return quantity_cast<unitless>(d1 / d2) + 1;
}
or to explicitly state what is the unit of our dimensionless value, e.g. `unitless`, `percent`, etc::
Dimensionless auto foo(Length auto d1, Length auto d2)
{
return d1 / d2 + dimensionless<unitless>(1);
}
There is one more important point to note here. As the the dimensionless quantity is more than just
a number, it is never implicitly converted back to the representation type. This means that the following
code will not compile::
auto v = std::exp(10q_m / 5q_m);
To make it compile fine we have to either explicitly get the value stored in the quantity::
auto v = std::exp(quantity_cast<unitless>(10q_m / 5q_m).count());
or use a mathematical wrapper function from `units` namespace::
auto v = units::exp(10q_m / 5q_m);
.. important::
Always remember to explicitly cast the quantity to the destination unit with `quantity_cast` before
calling `quantity::count()`!

View File

@@ -157,3 +157,59 @@ but often we would like to know a specific type too. We have two options here:
More information on this subject can be found in :ref:`Conversions and Casting` More information on this subject can be found in :ref:`Conversions and Casting`
chapter. chapter.
Dimensionless Quantities
------------------------
Whenever we divide two quantities of the same dimension we end up with a
:term:`dimensionless quantity` otherwise known as :term:`quantity of dimension one`::
static_assert(10q_km / 5q_km == 2);
static_assert(std::is_same_v<decltype(10q_km / 5q_km), quantity<dim_one, unitless, std::int64_t>>);
According to the official ISO definition `dim_one` is a dimension "for which all the
exponents of the factors corresponding to the base quantities in its quantity dimension
are zero".
.. seealso::
Reasoning for the above design is provided in
:ref:`Why a dimensionless quantity is not just an fundamental arithmetic type?`
To simplify the usage of the dimensionless quantity a following concept and alias template
are provided::
template<typename T>
concept Dimensionless = QuantityOf<T, dim_one>;
template<Unit U, Scalar Rep = double>
using dimensionless = quantity<dim_one, U, Rep>;
There are two special units provided for usage with such a quantity:
- `unitless` which is the :ref:`coherent unit` of dimensionless quantity and does not
provide any textual symbol (according to the ISO definition "the measurement units and
values of quantities of dimension one are numbers"),
- `percent` which has the symbol ``%`` and ``ratio(1, 100)`` of the `unitless` unit.
For example the following code::
std::cout << quantity_cast<percent>(50.q_m / 100.q_m) << '\n';
will print ``50 %`` to the console output.
Again, according to the ISO definition "such quantities convey more information than a
number". This is exactly what we observe in the above example. The value stored inside
the quantity, the text output, and the value returned by the `quantity::count()` member
function is ``50`` rather than ``0.5``. It means that dimensionless quantities behave
like all other quantities and store the value in terms of a ratio of a coherent unit.
This allows us to not loose precision when we divide quantities of the same dimensions
but with units having vastly different ratios, e.g.
`Dimensionless Hubble parameter <https://en.wikipedia.org/wiki/Hubble%27s_law#Dimensionless_Hubble_parameter>`_
is expressed as a ratio of kilometers and megaparsecs.
.. seealso::
More information on dimensionless quantities can be found in
:ref:`Implicit conversions of dimensionless quantities`.