Document number: P1050R0
Date: 2018-05-07
Audience: SG6, LEWG

# Fractional Numeric Type

## Abstract

This paper introduces a fractional number type which stores pairs of integer numerators and denominators. It avoids precision loss commonly associated with the storage of single-number quotients. It also helps express intent when initializing fixed-point types with the results of division operations.

## Introduction

Rational numbers provide a headache for digital number types such as the fundamental scalars. We already have `std::ratio` -- mostly as a necessary way of expressing different static scales in `std::chrono`. There has previously been discussion of sophisticated, general purpose bounded [P0101] and unbounded [P0101] rationals. Boost has a rational type [Boost].

This paper proposes a fractional type that is compatible with the compositional approach detailed in [P0554]. As such it is designed to provide a zero-overhead abstraction over pairs of integers while also being an expressive, user-friendly, general-purpose statically-sized number type when combined with other numeric components.

The reason for proposing a fractional type at this time is because it serves a specific role in initialization of the fixed-point numbers described in [P0037].

## Motivation

Class template, `fixed_point<>` from [P0037] can be initialized with integer and floating-point values. It is desirable that `fixed_point<>` values also be the quotient in a division operation. A `divide` function was previously proposed in [P0037]:

``````auto a = divide(1, 3);
``````

Straight away the question arises: what should be the resolution of `a`? The solution proposed in [P0106] is to determine the number of fractional digits from the number of fractional digits in the numerator and integer digits in the denominator. In the example above, `a` has 31 fractional digits because literal, `3`, has 31 integer digits. The result has value, 0.333333333022892475128173828125.

This solution is problematic when one considers all of the ways that `1` or `3` can be represented. For example, using `elastic_integer<>` [P0828] as the inputs

``````auto b = divide(elastic_integer<1>{1}, elastic_integer<2>{3});
``````

results in a `fixed_point` type with only 2 fractional digits. The resultant approximation of `1/3` is `0.25`. This is unlikely to be the desired result in most cases.

Specifying the output type is one solution:

``````auto c = divide<fixed_point<int, -16>>(1, 3);
auto d = divide<fixed_point<int, -16>>(elastic_integer<1>{1}, elastic_integer<2>{3});
``````

Both `c` and `d` have values, `0.3333282470703125`. However, it is not entirely clear that introducing a named function specifically for this purpose is necessary, nor whether the interface is sufficiently intuitive.

By introducing a fractional type, `fractional`

``````template<class Numerator, class Denominator>
struct fractional {
// ...

Numerator numerator;
Denominator denominator;
};
``````

we can express the same intent using only types:

``````auto a = fixed_point{fractional{1, 3}};
auto b = fixed_point{fractional{elastic_integer<1>{1}, elastic_integer<2>{3}}};
auto c = fixed_point<int, -16>{fractional{1, 3}};
auto d = fixed_point<int, -16>{fractional{elastic_integer<1>{1}, elastic_integer<2>{3}}};
``````

## Synopsis

``````template<class Numerator, class Denominator>
struct fractional {
using numerator_type = Numerator;
using denominator_type = Denominator;

explicit constexpr fractional(const Numerator& n, const Denominator& d);
explicit constexpr fractional(const Numerator& n);

template<class Scalar, _impl::enable_if_t<std::is_floating_point<Scalar>::value, int> = 0>
explicit constexpr operator Scalar() const;

numerator_type numerator;
denominator_type denominator = 1;
};

template<class Numerator, class Denominator>
fractional(const Numerator& n, const Denominator&)
-> fractional<Numerator, Denominator>;

template<class Numerator>
fractional(Numerator const& n)
-> fractional<Numerator, int>;

template<class Numerator, class Denominator>
constexpr auto reduce(fractional<Numerator, Denominator> const& f);

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator+(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs)
-> decltype(make_fractional(
lhs.numerator*rhs.denominator+rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator));

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator-(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs)
-> decltype(make_fractional(
lhs.numerator*rhs.denominator-rhs.numerator*lhs.denominator, lhs.denominator*rhs.denominator));

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator*(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs)
-> decltype(make_fractional(lhs.numerator*rhs.numerator, lhs.denominator*rhs.denominator));

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator/(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs)
-> decltype(make_fractional(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator));

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator==(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs);

template<class LhsNumerator, class LhsDenominator, class RhsNumerator, class RhsDenominator>
constexpr auto operator!=(
fractional<LhsNumerator, LhsDenominator> const& lhs,
fractional<RhsNumerator, RhsDenominator> const& rhs);
``````

## Reference Implementation

`fractional` is implemented as part of the CNL library (header, tests, fixed_point integration).