- Notifications
You must be signed in to change notification settings - Fork10
Fixed Point Arithmetic C++14 Library
License
mizvekov/fp
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This is a C++14 header-only fixed-point arithmetic library.
It's purpose is to wrap another type and provide fixed point arithmeticsupport on top of it.
It's designed to be able to wrap on top of all the built-in arithmetic types(integers and floating point types) as well as user defined arithmetic types.
Examples of other arithmetic types which are tested andpreliminary adapters are provided:
- boost
rational
- boost multiprecision
cpp_int
andcpp_bin_float
- David Stone's
bounded_integer
The following is a sample of what can be achieved by employing this library:
usingnamespacefp;auto x = make_fp<4,int >(3.25);auto y = make_fp<8,char>(0.75);auto z = x * y;// now z is of type fp_t<int,12>std::cout <<double(z);// prints 2.4375// using David Stone's bounded_integerusingnamespacebounded::literal;usingnamespacefp::constants;// import int_ shorthand for std::integral_constant// create a bounded fixed point integer with range [1, 100]// with initial value 30, then perform a virtual right shift// by 3.auto x =fp_t<bounded::integer<1,100>,0>{ 30_bi } >> int_<3>;// now x holds 3.75 and has range [0.125, 12.5]//ditto, range [2, 300], initial value 150, virtual right shift by 4auto y =fp_t<bounded::integer<2,300>,0>{ 150_bi } >> int_<4>;// now y holds 9.375 and has range [0.125, 18.75]auto z = x + y;std::cout <<double(z);// prints 13.125// below it shows that type information of the underlying type is not loststd::cout <<double(std::numeric_limits<decltype(z)>::min());// prints 0.25std::cout <<double(std::numeric_limits<decltype(z)>::max());// prints 31.25
It needs at least clang 3.4 to compile.Unit tests are included, and these can be built using CMake.
Example building and running tests on unix system:
cmake -DCMAKE_CXX_COMPILER=clang++<path to source>makectest
This is a single-header include only library, building is only required for tests.Just#include <fp/fp.hpp>
and everything is inside namespacefp
. The maintype isfp::fp_t
and there is also afp::make_fp
helper which does constructionwith type deduction. The namespacefp::constants
contains shorthandsint_
anduint_
which can be used for virtual shifting.
It implements a template class typefp_t<T, E>
whereT
is an underlyingarithmetic type andE
is an integer representing the binary point position.From now on these will be referred as the underlying type and the exponent,forT
andE
respectively.
It supports casting between integral and floating point typesand the whole set of arithmetic, bitwise and relational operators.All operations are constexpr themselves, although they are only usable assuch if the underlying type also implements them as constexpr.
A virtual shift may be performed by using astd::integral_constant
as ashift amount with the usual shift operators<<
and>>
. Convenience aliasesint_
anduint_
are provided under namespacefp::constants
All operations preserve the resulting type of the underlying type's operation.
For example, if you have an instancea
with typefp_t<A, EXP_A>
, and an instanceb with typefp_t<B, EXP_B>
, and you multiply them together, the type of the expressiona * b
will befp_t<decltype(A{} * B{}), EXP_A + EXP_B>
. In the specific case thatA
ischar
,B
isint
andEXP_A
andEXP_B
are2
and3
, the resultingtype will befp_t<int, 5>
, sincechar{} * int{}
has typeint
, according to normalC++ conversion rules.
Similar thing happens with unary operations, the expression+fp_t<A, EXP_A>{}
has typefp_t<decltype(+A{}), EXP_A>
.
The result of a virtual shift has underlying type unchanged, and just theexponent is modified. The expressionfp_t<A, EXP_A>{} >> int_<SA>
hastypefp_t<A, EXP_A + SA>
.
All the unary operations preserve the exponent, and in the case ofbinary operations, the exponent of the result depends on the operationbeing performed.
For addition, subtraction, modulus and the bitwise operators, the result hasexponent equal to the greatest of the exponents of the operands, and theoperation is carried out as if the operand with the smallest exponent wascast to that same exponent. For example, the result of the expressionfp_t<int, 4>(1) + fp_t<int, 8>(2)
has typefp_t<int, 8>
For multiplication and division, the exponent of the result is equal to thesum and subtraction of the exponents of the operands respectively.This implies the expressionfp_t<int, 4>{} * fp_t<int, 8>{}
has typefp_t<int, 12>
, and with division instead, the resulting type would befp_t<int, -4>
When using the relational operators betweenfp_t numbers with differentexponents, the operand with the greatest exponent is converted so it has thesame exponent as the other one, and they are then compared.This avoids a more expensive operation, and means that only leastsignificant bits are discarded.
At a minimum, the underlying type must be default constructible,copy constructible and support left and right shift by astd::integral_constant
. The other operators are optional, and ifthey are not supported, the composed type will not support them.For example, if there is no modulus operator implemented forT
,thenfp_t<T>
will not support modulus either.
ISC
Copyright (c) 2018, Matheus Izvekovmizvekov@gmail.com
All rights reserved.