diff --git a/docs/faq.rst b/docs/faq.rst index e4957155..b531a0eb 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -28,6 +28,34 @@ different dimensions (i.e. height, width, and depth) all of them will just be 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); + +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``? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/framework/conversions_and_casting.rst b/docs/framework/conversions_and_casting.rst index f72c4d7c..04855279 100644 --- a/docs/framework/conversions_and_casting.rst +++ b/docs/framework/conversions_and_casting.rst @@ -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" vs "floating-point" logic please refer to the :ref:`Using Custom Representation Types` 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) { + // ... + } + +or:: + + const auto fill_time_left = (box.height / box.fill_level(measured_mass) - + dimensionless) * fill_time; + +This is why it was decided to allow the ``dimensionless`` 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(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(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(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()`! diff --git a/docs/framework/quantities.rst b/docs/framework/quantities.rst index a5191f03..06947362 100644 --- a/docs/framework/quantities.rst +++ b/docs/framework/quantities.rst @@ -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` 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>); + +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 + concept Dimensionless = QuantityOf; + + template + using dimensionless = quantity; + +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(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 `_ +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`.