Introduction
The fixed_point library provides a header-only C++11 API for approximating real numbers using binary fixed-point arithmetic. It forms the reference implementation for a standard library proposal presented in paper, P0037 and is developed as part of study groups, SG14 and SG6. Installation instructions are provided on the project page.
Features
Header, sg14/fixed_point, defines class template, fixed_point, with:
- a simple, flexible generic design;
- operator overloads that interface fixed_point with other numeric types;
- a set of function templates, (e.g. multiply), for fine-grain control.
Header, sg14/num_traits, contains additions (such as set_digits) that support widening of arithmetic types in order to deal with precision loss.
Auxiliary modules:
Examples
The following examples can be found in the test suite.
Declaration
The fixed_point type extends the behavior of integer types in a natural way. It represents a real number using an integer type, scaled by a power of two.
#include <sg14/fixed_point>
#include <iostream>
void declaration_example()
{
static_assert(is_same<decltype(x), decltype(y)>::value, "");
cout << x.data() << endl;
cout << x << endl;
x /= 2;
cout << x << endl;
}
Arithmetic Operators
Specializations of fixed_point behave a lot like native C/C++ numeric types. Operators are designed to behave in an way which is both predictable and efficient.
void basic_arithmetic_example()
{
auto pi = fixed_point<int32_t, -28>{3.1415926535};
auto tau = pi*2;
static_assert(is_same<decltype(tau), fixed_point<int32_t, -28>>::value, "");
cout << tau << endl;
auto degrees = tau*(180/3.1415926534);
static_assert(is_same<decltype(degrees), double>::value, "");
cout << degrees << '\n';
}
Arithmetic Functions
But one size does not fit all. Different applications of the same operation might call for different trade-offs between storage, precision, safety and speed. Named functions - such as multiply - provide fine-tuned control over arithmetic results.
#include <iomanip>
void advanced_arithmetic_example()
{
auto x = fixed_point<uint8_t, -4>{15.9375};
cout << fixed_point<uint8_t, -4>{x*x} << endl;
auto xx = x*x;
static_assert(is_same<decltype(xx), fixed_point<int, -8>>::value, "");
cout << setprecision(12) << xx << endl;
auto named_xx = multiply(x, x);
static_assert(is_same<decltype(named_xx), fixed_point<uint16_t, -8>>::value, "");
cout << named_xx << endl;
}
Extensible
Because one size does not fit all, fixed_point is designed to make it easy to tailor new arithmetic types. The elastic_fixed_point type illustrates this. As each calculation requires more digits, so the results of elastic_fixed_point operations allocate more storage.
#include <sg14/auxiliary/elastic_integer.h>
void elastic_example1()
{
auto a = elastic_integer<6, int8_t>{ 63 };
auto aa = a*a;
static_assert(is_same<decltype(aa), elastic_integer<12, int8_t >> ::value, "");
static_assert(sizeof(aa)==2, "");
auto a2 = a+a;
static_assert(is_same<decltype(a2), elastic_integer<7, int8_t >> ::value, "");
}
template<int IntegerDigits, int FractionalDigits, class Narrowest>
using elastic_fixed_point = fixed_point<elastic_integer<IntegerDigits+FractionalDigits, Narrowest>, -FractionalDigits>;
void elastic_example2()
{
auto b = elastic_fixed_point<4, 28, unsigned>{15.9375};
auto bb = b*b;
cout << bb << endl;
static_assert(is_same<decltype(bb), elastic_fixed_point<8, 56, unsigned>>::value, "");
}