Lumiera  0.pre.03
»edit your freedom«
rational.hpp File Reference

Go to the source code of this file.

Description

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

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<NUM> (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.

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 also
Rational_test
zoom-window.hpp
timevalue.hpp

Definition in file rational.hpp.

#include "lib/integral.hpp"
#include "lib/util-quant.hpp"
#include <cmath>
#include <limits>
#include <stdint.h>
#include <boost/rational.hpp>

Typedefs

using Rat = boost::rational< int64_t >
 

Functions

bool can_represent_Product (int64_t a, int64_t b)
 
bool can_represent_Product (Rat a, Rat b)
 
bool can_represent_Sum (Rat a, Rat b)
 
util::Rat operator""_r (unsigned long long num)
 user defined literal for constant rational numbers. More...
 
int64_t reQuant (int64_t num, int64_t den, int64_t u)
 Re-Quantise a number into a new grid, truncating to the next lower grid point. More...
 
Rat reQuant (Rat src, int64_t u)
 re-Quantise a rational number to a (typically smaller) denominator. More...
 

Function Documentation

◆ reQuant() [1/2]

int64_t util::reQuant ( int64_t  num,
int64_t  den,
int64_t  u 
)
inline

Re-Quantise a number into a new grid, truncating to the next lower grid point.

Remarks
Grid-aligned values can be interpreted as rational numbers (integer fractions), where the quantiser corresponds to the denominator and the numerator counts the number of grid steps. To work both around precision problems and the danger of integer wrap-around, the integer division is performed on the old value and then the re-quantisation done on the remainder, using 128bit floating point for maximum precision. This operation can also be used to re-form a fraction to be cast in terms of the new quantiser; this introduces a tiny error, but typically allows for safe or simplified calculations.
Parameters
numthe count in old grid-steps (#den) or the numerator
denthe old quantiser or the denominator of a fraction
uthe new quantiser or the new denominator to use
Returns
the adjusted numerator, so that the fraction with u will be almost the same than dividing #num by #den

Definition at line 129 of file rational.hpp.

References util::iDiv(), and util::reQuant().

Referenced by util::reQuant().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ reQuant() [2/2]

Rat util::reQuant ( Rat  src,
int64_t  u 
)
inline

re-Quantise a rational number to a (typically smaller) denominator.

Parameters
uthe new denominator to use
Warning
this is a lossy operation and possibly introduces an error of up to 1/u
Remarks
Rational numbers with large numerators can be »poisonous«, causing numeric overflow when used, even just additively. This function can thus be used to _"sanitise"_ a number, and thus accept a small error while preventing overflow.
Note
using extended-precision floating point to get close to the quantiser even for large denominator. Some platforms fall back to double, leading to extended error bounds

Definition at line 159 of file rational.hpp.

References util::reQuant().

+ Here is the call graph for this function:

◆ operator""_r()

util::Rat operator""_r ( unsigned long long  num)
inline

user defined literal for constant rational numbers.

Rat twoThirds = 2_r/3;

Definition at line 174 of file rational.hpp.