/* RATIONAL.hpp - support for precise rational arithmetics Copyright (C) Lumiera.org 2022, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file rational.hpp ** Rational number support, based on `boost::rational`. ** As an extension to integral arithmetics, rational numbers can be defined ** as a pair (numerator, denominator); since most calculations imply multiplication ** by common factors, each calculation will be followed by normalisation to greatest ** common denominator, to keep numbers within value range. Obviously, this incurs ** a significant performance penalty — while on the other hand allowing for lossless ** computations on fractional scales, which can be notoriously difficult to handle ** with floating point numbers. The primary motivation for using this number format ** is for handling fractional time values properly, e.g 1/30 sec or 1/44100 sec. ** ** The underlying implementation from boost::rational can be parametrised with various ** integral data types; since our time handling is based on 64bit integers, we mainly ** use the specialisation `boost::rational`. ** ** @note all compatible integral types can be automatically converted to rational ** numbers, which is a lossless conversion. The opposite is not true: to get ** a "ordinary" number — be it integral or floating point — an explicit ** conversion using `rational_cast (fraction)` is necessary, which ** performs the division of `numerator/denominator` in the target value domain. ** ** # Perils of fractional arithmetics ** ** While the always precise results of integral numbers might seem compelling, the ** danger of _numeric overflow_ is significantly increased by fractional computations. ** Most notably, this danger is *not limited to large numbers*. Adding two fractional ** number requires multiplications with both denominators, which can overflow easily. ** Thus, for every given fractional number, there is a class of »dangerous counterparts« ** which can not be added without derailing the computation, leading to arbitrary wrong ** results without detectable failure. And these problematic counterparts are distributed ** _over the whole valid numeric range._ To give an extreme example, any numbers of the ** form `n / INT_MAX` can not be added or subtracted with any other rational number > 1, ** while being themselves perfectly valid and representable. ** \par rule of thumb ** Use fractional arithmetics only where it is possible to control the denominators involved. ** Never use them for computations drawing from arbitrary (external) input. ** ** @see zoom-window.hpp ** @see timevalue.hpp */ #ifndef LIB_RATIONAL_H #define LIB_RATIONAL_H #include #include namespace util { using Rat = boost::rational; using boost::rational_cast; } // namespace util /** * user defined literal for constant rational numbers. * \code * Rat twoThirds = 2_r/3; * \endcode */ inline util::Rat operator""_r (unsigned long long num) { return util::Rat{num}; } #endif /*LIB_RATIONAL_H*/