Last active
November 15, 2016 04:11
-
-
Save johnmcfarlane/d3adca0da5b5a06d87d60475fe063275 to your computer and use it in GitHub Desktop.
single-header version of header, elastic.h, from johnmcfarlane/fixed_point
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief essential definitions related to the `sg14::elastic` type | |
#define SG14_USE_INT128 | |
#if !defined(SG14_ELASTIC_H) | |
#define SG14_ELASTIC_H 1 | |
// Copyright John McFarlane 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief essential definitions related to the `sg14::elastic_integer` type | |
#if !defined(SG14_ELASTIC_INTEGER_H) | |
#define SG14_ELASTIC_INTEGER_H 1 | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
// definitions that are directly required by more than one header of the API | |
#if !defined(SG14_COMMON_H) | |
#define SG14_COMMON_H 1 | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief trait definitions related to the `sg14::fixed_point` type | |
#if !defined(SG14_TYPE_TRAITS_H) | |
#define SG14_TYPE_TRAITS_H 1 | |
#include <type_traits> | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// import selected <type_traits> definitions from std namespace | |
// make_signed | |
template<class T> | |
struct make_signed; | |
template<class T> | |
struct make_signed : std::make_signed<T> { | |
}; | |
// make_unsigned | |
template<class T> | |
struct make_unsigned; | |
template<class T> | |
struct make_unsigned : std::make_unsigned<T> { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// Clang/GCC hacks to get 128-bit integers working with fixed_point | |
#if defined(SG14_INT128_ENABLED) | |
// sg14::make_signed | |
template<> | |
struct make_signed<SG14_INT128> { | |
using type = SG14_INT128; | |
}; | |
template<> | |
struct make_signed<SG14_UINT128> { | |
using type = SG14_INT128; | |
}; | |
// sg14::make_signed | |
template<> | |
struct make_unsigned<SG14_INT128> { | |
using type = SG14_UINT128; | |
}; | |
template<> | |
struct make_unsigned<SG14_UINT128> { | |
using type = SG14_UINT128; | |
}; | |
#endif | |
} | |
#endif // SG14_TYPE_TRAITS_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief specialization of `std::numeric_limits` for 128-bit integer types | |
#if !defined(SG14_LIMITS_H) | |
#define SG14_LIMITS_H 1 | |
// Copyright John McFarlane 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
#ifndef SG14_CONFIG_H | |
#define SG14_CONFIG_H | |
//////////////////////////////////////////////////////////////////////////////// | |
// SG14_INT128_ENABLED macro definition | |
#if defined(SG14_INT128_ENABLED) | |
#error SG14_INT128_ENABLED already defined | |
#endif | |
#if defined(SG14_USE_INT128) | |
// GCC/Clang 64-bit builds support 128-bit integer through __int128 type | |
#if defined(__SIZEOF_INT128__) | |
#define SG14_INT128_ENABLED | |
using SG14_INT128 = __int128; | |
using SG14_UINT128 = unsigned __int128; | |
#endif | |
#endif // defined(SG14_USE_INT128) | |
//////////////////////////////////////////////////////////////////////////////// | |
// SG14_EXCEPTIONS_ENABLED macro definition | |
#if defined(SG14_EXCEPTIONS_ENABLED) | |
#error SG14_EXCEPTIONS_ENABLED already defined | |
#endif | |
#if defined(_MSC_VER) | |
#if defined(_CPPUNWIND) | |
#define SG14_EXCEPTIONS_ENABLED | |
#endif | |
#elif defined(__clang__) || defined(__GNUG__) | |
#if defined(__EXCEPTIONS) | |
#define SG14_EXCEPTIONS_ENABLED | |
#endif | |
#else | |
#define SG14_EXCEPTIONS_ENABLED | |
#endif | |
#endif // SG14_CONFIG_H | |
#include <climits> | |
#include <cstdint> | |
#include <limits> | |
#if defined(SG14_INT128_ENABLED) | |
namespace std { | |
template<> | |
struct numeric_limits<SG14_INT128> : numeric_limits<long long> { | |
static const int digits = CHAR_BIT*sizeof(SG14_INT128)-1; | |
static const int digits10 = 38; | |
struct _s { | |
constexpr _s(uint64_t upper, uint64_t lower) : value(lower + (SG14_INT128{upper} << 64)) {} | |
constexpr operator SG14_INT128() const { return value; } | |
SG14_INT128 value; | |
}; | |
static constexpr SG14_INT128 min() | |
{ | |
return _s(0x8000000000000000, 0x0000000000000000); | |
} | |
static constexpr SG14_INT128 max() | |
{ | |
return _s(0x7fffffffffffffff, 0xffffffffffffffff); | |
} | |
static constexpr SG14_INT128 lowest() | |
{ | |
return min(); | |
} | |
}; | |
template<> | |
struct numeric_limits<SG14_UINT128> : numeric_limits<unsigned long long> { | |
static const int digits = CHAR_BIT*sizeof(SG14_INT128); | |
static const int digits10 = 38; | |
struct _s { | |
constexpr _s(uint64_t upper, uint64_t lower) : value(lower + (SG14_UINT128{upper} << 64)) {} | |
constexpr operator SG14_INT128() const { return value; } | |
SG14_UINT128 value; | |
}; | |
static constexpr SG14_INT128 min() | |
{ | |
return 0; | |
} | |
static constexpr SG14_INT128 max() | |
{ | |
return _s(0xffffffffffffffff, 0xffffffffffffffff); | |
} | |
static constexpr SG14_INT128 lowest() | |
{ | |
return min(); | |
} | |
}; | |
} | |
#endif // SG14_INT128_ENABLED | |
#endif // SG14_LIMITS_H | |
namespace sg14 { | |
namespace _impl { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::max | |
template<class T> | |
constexpr T max(T a, T b) | |
{ | |
return (a<b) ? b : a; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::max | |
template<class T> | |
constexpr T min(T a, T b) | |
{ | |
return (a<b) ? a : b; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::common_type_t | |
// pre-C++14 common_type_t | |
template<class ... T> | |
using common_type_t = typename std::common_type<T ...>::type; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::identical - compiles iff same type; returns true iff equal | |
template<typename A, typename B> | |
constexpr bool identical(const A& a, const B& b) | |
{ | |
static_assert(std::is_same<A, B>::value, "different types"); | |
return a==b; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::is_integer_or_float - trait to identify 'traditional' arithmetic concept | |
template<class T> | |
struct is_integer_or_float : std::integral_constant< | |
bool, | |
std::numeric_limits<T>::is_integer || std::is_floating_point<T>::value> { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// operator helpers | |
//////////////////////////////////////////////////////////////////////////////// | |
// operation tags | |
struct negate_tag; | |
struct add_tag; | |
struct subtract_tag; | |
struct multiply_tag; | |
struct divide_tag; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::op | |
template<class OperationTag, class ... Operands> | |
struct op; | |
template<class Rhs> | |
struct op<negate_tag, Rhs> { | |
constexpr auto operator()(const Rhs& rhs) const -> decltype(-rhs) | |
{ | |
return -rhs; | |
} | |
}; | |
template<class Lhs, class Rhs> | |
struct op<add_tag, Lhs, Rhs> { | |
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs+rhs) | |
{ | |
return lhs+rhs; | |
} | |
}; | |
template<class Lhs, class Rhs> | |
struct op<subtract_tag, Lhs, Rhs> { | |
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs-rhs) | |
{ | |
return lhs-rhs; | |
} | |
}; | |
template<class Lhs, class Rhs> | |
struct op<multiply_tag, Lhs, Rhs> { | |
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs*rhs) | |
{ | |
return lhs*rhs; | |
} | |
}; | |
template<class Lhs, class Rhs> | |
struct op<divide_tag, Lhs, Rhs> { | |
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs/rhs) | |
{ | |
return lhs/rhs; | |
} | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::rep_op_fn | |
template<class OperationTag, class Lhs, class Rhs> | |
constexpr auto op_fn(const Lhs& lhs, const Rhs& rhs) | |
-> decltype(op<OperationTag, Lhs, Rhs>()(lhs, rhs)) | |
{ | |
return op<OperationTag, Lhs, Rhs>()(lhs, rhs); | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::rep_op_result | |
template<class OperationTag, class Lhs, class Rhs> | |
using op_result = decltype(op_fn<OperationTag, Lhs, Rhs>(std::declval<Lhs>(), std::declval<Rhs>())); | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::make_signed - std::make_signed with IsSigned parameter | |
template<class T, bool IsSigned> | |
struct make_signed; | |
template<class T> | |
struct make_signed<T, true> : sg14::make_signed<T> { | |
}; | |
template<class T> | |
struct make_signed<T, false> : sg14::make_unsigned<T> { | |
}; | |
template<class T, bool IsSigned> | |
using make_signed_t = typename make_signed<T, IsSigned>::type; | |
} | |
} | |
#endif // SG14_COMMON_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief additions to <cstdint> proposed in P0381 | |
#if !defined(SG14_CSTDINT_H) | |
#define SG14_CSTDINT_H 1 | |
#include <climits> | |
#include <cstdint> | |
#include <tuple> | |
#include <type_traits> | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
using _width_type = unsigned; | |
//////////////////////////////////////////////////////////////////////////////// | |
// width - new type property which returns number of bits of information | |
/// \brief provides width of numeric type | |
/// | |
/// \tparam T given numeric type | |
/// | |
/// \remarks If \c T is a fixed-point numeric type such as an integral type, | |
/// \c width<T>::value is the width of T in bits. | |
/// \remarks The width is defined as the number of digits including any sign bit. | |
/// \remarks The template may be specialized for custom types. | |
template<class T> | |
struct width; | |
template<> | |
struct width<char> : std::integral_constant<_width_type, sizeof(char)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<wchar_t> : std::integral_constant<_width_type, sizeof(wchar_t)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<signed char> : std::integral_constant<_width_type, sizeof(signed char)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<unsigned char> : std::integral_constant<_width_type, sizeof(unsigned char)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<signed short> : std::integral_constant<_width_type, sizeof(signed short)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<unsigned short> : std::integral_constant<_width_type, sizeof(unsigned short)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<signed int> : std::integral_constant<_width_type, sizeof(signed int)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<unsigned int> : std::integral_constant<_width_type, sizeof(unsigned int)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<signed long> : std::integral_constant<_width_type, sizeof(signed long)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<unsigned long> : std::integral_constant<_width_type, sizeof(unsigned long)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<signed long long> : std::integral_constant<_width_type, sizeof(signed long long)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<unsigned long long> : std::integral_constant<_width_type, sizeof(unsigned long long)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<float> : std::integral_constant<_width_type, sizeof(float)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<double> : std::integral_constant<_width_type, sizeof(double)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<long double> : std::integral_constant<_width_type, sizeof(long double)*CHAR_BIT> { | |
}; | |
namespace _type_traits_impl { | |
//////////////////////////////////////////////////////////////////////////////// | |
// built-in families | |
#if defined(SG14_INT128_ENABLED) | |
using signed_family = std::tuple<std::int8_t, std::int16_t, std::int32_t, std::int64_t, SG14_INT128>; | |
using unsigned_family = std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t, SG14_UINT128>; | |
#else | |
using signed_family = std::tuple<std::int8_t, std::int16_t, std::int32_t, std::int64_t>; | |
using unsigned_family = std::tuple<std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>; | |
#endif | |
#if defined(_MSC_VER) | |
using float_family = std::tuple<float, double>; | |
#else | |
using float_family = std::tuple<float, double, long double>; | |
#endif | |
//////////////////////////////////////////////////////////////////////////////// | |
// first_fit | |
template<_width_type MinNumBits, class, class ... FamilyMembers> | |
struct first_fit_helper; | |
template<_width_type MinNumBits> | |
struct first_fit_helper<MinNumBits, void> { | |
using type = void; | |
}; | |
template<_width_type MinNumBits, class FamilyMembersHead, class ... FamilyMembersTail> | |
struct first_fit_helper< | |
MinNumBits, | |
typename std::enable_if<width<FamilyMembersHead>::value>=MinNumBits>::type, | |
FamilyMembersHead, FamilyMembersTail ...> { | |
using type = FamilyMembersHead; | |
}; | |
template<_width_type MinNumBits, class FamilyMembersHead, class ... FamilyMembersTail> | |
struct first_fit_helper< | |
MinNumBits, | |
typename std::enable_if<width<FamilyMembersHead>::value<MinNumBits>::type, | |
FamilyMembersHead, FamilyMembersTail ...> { | |
using _tail_base = first_fit_helper<MinNumBits, void, FamilyMembersTail ...>; | |
using type = typename _tail_base::type; | |
}; | |
template<_width_type MinNumBits, class Family> | |
struct first_fit; | |
template<_width_type MinNumBits, class ... FamilyMembers> | |
struct first_fit<MinNumBits, std::tuple<FamilyMembers...>> { | |
using type = typename first_fit_helper<MinNumBits, void, FamilyMembers...>::type; | |
static_assert(!std::is_void<type>::value, "cannot find wide enough type"); | |
}; | |
} | |
#if (__cplusplus>=201402L) | |
//////////////////////////////////////////////////////////////////////////////// | |
// width_v - equals number of bits of information in given type | |
/// \brief provides width of numeric type | |
/// | |
/// \tparam T given numeric type | |
/// | |
/// \remarks The width is defined as the number of digits including any sign bit. | |
template<class T> | |
constexpr unsigned width_v = width<T>::value; | |
#endif | |
/// resizes a type; | |
/// can be specialized for any type for which resizing that type makes sense | |
/// | |
/// \sa set_width_t | |
template<class Archetype, _width_type MinNumBits> | |
struct set_width; | |
// sg14::set_width specialized for char/wchar_t | |
template<_width_type MinNumBits> | |
struct set_width<char, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, typename std::conditional<std::is_signed<char>::value, _type_traits_impl::signed_family, _type_traits_impl::unsigned_family>::type> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<wchar_t, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, typename std::conditional<std::is_signed<char>::value, _type_traits_impl::signed_family, _type_traits_impl::unsigned_family>::type> { | |
}; | |
// sg14::set_width specialized for 8-bit built-in integers | |
template<_width_type MinNumBits> | |
struct set_width<signed char, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::signed_family> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<unsigned char, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::unsigned_family> { | |
}; | |
// sg14::set_width specialized for 16-bit built-in integers | |
template<_width_type MinNumBits> | |
struct set_width<signed short, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::signed_family> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<unsigned short, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::unsigned_family> { | |
}; | |
// sg14::set_width specialized for 32-bit built-in integers | |
template<_width_type MinNumBits> | |
struct set_width<signed int, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::signed_family> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<unsigned int, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::unsigned_family> { | |
}; | |
// sg14::set_width specialized for 64-bit built-in integers | |
template<_width_type MinNumBits> | |
struct set_width<signed long, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::signed_family> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<unsigned long, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::unsigned_family> { | |
}; | |
// sg14::set_width specialized for 64-bit built-in integers | |
template<_width_type MinNumBits> | |
struct set_width<signed long long, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::signed_family> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<unsigned long long, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::unsigned_family> { | |
}; | |
// sg14::set_width specialized for float | |
template<_width_type MinNumBits> | |
struct set_width<float, MinNumBits> : _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::float_family> { | |
}; | |
// sg14::set_width specialized for double | |
template<_width_type MinNumBits> | |
struct set_width<double, MinNumBits> : _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::float_family> { | |
}; | |
// sg14::set_width specialized for long double | |
template<_width_type MinNumBits> | |
struct set_width<long double, MinNumBits> | |
: _type_traits_impl::first_fit<MinNumBits, _type_traits_impl::float_family> { | |
}; | |
/// \brief resizes a type | |
/// | |
/// \tparam Archetype the type to resize | |
/// \tparam MinNumBits the desired width in bits | |
/// | |
/// \par Examples | |
/// | |
/// To resize a native-sized unsigned int to 2-bytes: | |
/// \snippet snippets.cpp use set_width 1 | |
/// | |
/// To resize a signed byte type to a built-in signed type of at least 5 bytes: | |
/// \snippet snippets.cpp use set_width 2 | |
/// | |
/// To resize a signed, 1-byte fixed-point type to a fixed-point type of at least 3 bytes: | |
/// \snippet snippets.cpp use set_width 3 | |
template<class Archetype, _width_type MinNumBits> | |
using set_width_t = typename set_width<Archetype, MinNumBits>::type; | |
//////////////////////////////////////////////////////////////////////////////// | |
// Clang/GCC hacks to get 128-bit integers working with fixed_point | |
#if defined(SG14_INT128_ENABLED) | |
// sg14::set_width | |
template<_width_type MinNumBits> | |
struct set_width<SG14_INT128, MinNumBits> : set_width<signed, MinNumBits> { | |
}; | |
template<_width_type MinNumBits> | |
struct set_width<SG14_UINT128, MinNumBits> : set_width<unsigned, MinNumBits> { | |
}; | |
// sg14::width | |
template<> | |
struct width<SG14_INT128> : std::integral_constant<_width_type, sizeof(SG14_INT128)*CHAR_BIT> { | |
}; | |
template<> | |
struct width<SG14_UINT128> : std::integral_constant<_width_type, sizeof(SG14_UINT128)*CHAR_BIT> { | |
}; | |
#endif | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::scale<> | |
template<typename Integer> | |
struct scale; | |
template<typename Integer> | |
struct scale { | |
using result_type = decltype(std::declval<Integer>()*std::declval<Integer>()); | |
static constexpr result_type pown(int base, int exp) { | |
return exp | |
? pown(base, exp - 1) * static_cast<result_type>(base) | |
: static_cast<result_type>(1); | |
} | |
static constexpr result_type pow2(int exp) { | |
return result_type{1} << exp; | |
} | |
static constexpr result_type pow(int base, int exp) | |
{ | |
return (base==2) ? pow2(exp) : pown(base, exp); | |
} | |
constexpr result_type operator()(const Integer& i, int base, int exp) const { | |
return (exp < 0) ? i / pow(base, -exp) : i * pow(base, exp); | |
} | |
}; | |
} | |
#endif // SG14_CSTDINT_H | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
/// \brief literal integer type that encodes its width in bits within its type | |
/// | |
/// \tparam Digits a count of the number of digits needed to express the number | |
/// \tparam Archetype an integer type (typically `signed` or `unsigned`) | |
/// that acts as a hint about what integer type should actually be used to represent the value | |
/// | |
/// \note Arithmetic operations result in types with an adjusted Digits parameter accordingly. | |
/// For instance, when two \ref elastic_integer values are multiplied together, | |
/// the resultant type has Digits set to the sum of the operands. | |
/// | |
/// \sa elastic | |
template<int Digits, class Archetype = int> | |
class elastic_integer { | |
public: | |
/// alias to template parameter, \a Digits | |
static constexpr int digits = Digits; | |
/// alias to template parameter, \a Archetype | |
using archetype = Archetype; | |
/// width of value | |
static constexpr int width = digits+std::numeric_limits<Archetype>::is_signed; | |
private: | |
static constexpr int _min_width = sg14::width<Archetype>::value; | |
static constexpr int _rep_width = _impl::max(_min_width, width); | |
public: | |
/// the actual type used to store the value; closely related to Archetype but may be a different width | |
using rep = set_width_t<Archetype, _rep_width>; | |
/// common copy constructor | |
constexpr elastic_integer(const elastic_integer& rhs) | |
: _r(rhs._r) | |
{ | |
} | |
/// construct from integer type | |
template<class Number, typename std::enable_if<std::numeric_limits<Number>::is_specialized, int>::type Dummy = 0> | |
constexpr elastic_integer(Number n) | |
: _r(static_cast<rep>(n)) | |
{ | |
} | |
/// constructor taking an elastic_integer type | |
template<int FromWidth, class FromArchetype> | |
explicit constexpr elastic_integer(const elastic_integer<FromWidth, FromArchetype>& rhs) | |
:_r(rhs) | |
{ | |
} | |
/// copy assignment operator taking a floating-point type | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0> | |
elastic_integer& operator=(S s) | |
{ | |
_r = floating_point_to_rep(s); | |
return *this; | |
} | |
/// returns value | |
template<class S> | |
explicit constexpr operator S() const | |
{ | |
return static_cast<S>(_r); | |
} | |
/// returns internal representation of value | |
constexpr rep data() const | |
{ | |
return _r; | |
} | |
/// creates an instance given the underlying representation value | |
static constexpr elastic_integer from_data(rep r) | |
{ | |
return elastic_integer(r); | |
} | |
private: | |
//////////////////////////////////////////////////////////////////////////////// | |
// variables | |
rep _r; | |
}; | |
namespace _elastic_integer_impl { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_elastic_integer_impl::is_elastic_integer | |
template<class ElasticInteger> | |
struct is_elastic_integer; | |
template<class ElasticInteger> | |
struct is_elastic_integer : std::false_type { | |
}; | |
template<int Digits, class Archetype> | |
struct is_elastic_integer<elastic_integer<Digits, Archetype>> : std::true_type { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_elastic_integer_impl::are_integer_class_operands - basically identifies | |
// operands that should go into a function defined here; filters out fixed-point | |
template<class Lhs, class Rhs> | |
struct are_integer_class_operands { | |
static constexpr int integer_class = is_elastic_integer<Lhs>::value+is_elastic_integer<Rhs>::value; | |
static constexpr int integer_or_float = | |
_impl::is_integer_or_float<Lhs>::value+_impl::is_integer_or_float<Rhs>::value; | |
static constexpr bool value = (integer_class>=1) && (integer_or_float==2); | |
}; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// bitwise operators | |
// operator<< | |
template<int LhsDigits, class LhsArchetype, class Rhs> | |
constexpr auto operator<<(const elastic_integer<LhsDigits, LhsArchetype>& lhs, const Rhs& rhs) | |
-> elastic_integer<LhsDigits, LhsArchetype> | |
{ | |
return elastic_integer<LhsDigits, LhsArchetype>::from_data(lhs.data() << rhs); | |
} | |
template<class Lhs, int RhsDigits, class RhsArchetype> | |
constexpr auto operator<<(const Lhs& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> decltype(lhs << 0) | |
{ | |
return lhs << rhs.data(); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// comparison operators | |
#define SG14_ELASTIC_INTEGER_COMPARISON_OP(OP) \ | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype>\ | |
constexpr auto \ | |
operator OP (const elastic_integer<LhsDigits, LhsArchetype>& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) \ | |
-> decltype(lhs.data() OP rhs.data()) \ | |
{ \ | |
return lhs.data() OP rhs.data(); \ | |
} \ | |
\ | |
template<int LhsDigits, class LhsArchetype, class RhsInteger> \ | |
constexpr auto operator OP (const elastic_integer<LhsDigits, LhsArchetype>& lhs, const RhsInteger& rhs) \ | |
-> typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer, decltype(lhs.data() OP rhs)>::type \ | |
{ \ | |
return lhs.data() OP rhs; \ | |
} \ | |
\ | |
template<int LhsDigits, class LhsArchetype, class RhsFloat> \ | |
constexpr auto operator OP (const elastic_integer<LhsDigits, LhsArchetype>& lhs, const RhsFloat& rhs) \ | |
-> typename std::enable_if<std::is_floating_point<RhsFloat>::value, decltype(lhs.data() OP rhs)>::type \ | |
{ \ | |
return lhs.data() OP rhs; \ | |
} \ | |
\ | |
template<class LhsInteger, int RhsDigits, class RhsArchetype> \ | |
constexpr auto operator OP (const LhsInteger& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) \ | |
-> typename std::enable_if<std::numeric_limits<LhsInteger>::is_integer, decltype(lhs OP rhs.data())>::type \ | |
{ \ | |
return lhs OP rhs.data(); \ | |
} \ | |
\ | |
template<class LhsFloat, int RhsDigits, class RhsArchetype> \ | |
constexpr auto operator OP (const LhsFloat& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) \ | |
-> typename std::enable_if<std::is_floating_point<LhsFloat>::value, decltype(lhs OP rhs.data())>::type \ | |
{ \ | |
return lhs OP rhs.data(); \ | |
} | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(==); | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(!=); | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(<); | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(>); | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(<=); | |
SG14_ELASTIC_INTEGER_COMPARISON_OP(>=); | |
//////////////////////////////////////////////////////////////////////////////// | |
// arithmetic operators | |
namespace _elastic_integer_impl { | |
//////////////////////////////////////////////////////////////////////////////// | |
// policies | |
template<class Operation, class LhsTraits, class RhsTraits> | |
struct policy; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::add_tag, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits)+1; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
// TODO: if both operands are unsigned and LhsTraits::digits>=RhsTraits::digits, | |
// TODO: then digits=LhsTraits::digits and is_signed=false | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::subtract_tag, LhsTraits, RhsTraits> { | |
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits); | |
static constexpr bool is_signed = true; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::multiply_tag, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits+RhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
template<class LhsTraits, class RhsTraits> | |
struct policy<_impl::divide_tag, LhsTraits, RhsTraits> { | |
static constexpr int digits = LhsTraits::digits; | |
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// traits | |
template<class Param> | |
struct traits { | |
static constexpr int digits = std::numeric_limits<Param>::digits; | |
static constexpr bool is_signed = std::numeric_limits<Param>::is_signed; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// operate_params | |
template<class OperationTag, class Lhs, class Rhs> | |
struct operate_params { | |
using lhs_traits = traits<Lhs>; | |
using rhs_traits = traits<Rhs>; | |
using policy = typename _elastic_integer_impl::policy<OperationTag, lhs_traits, rhs_traits>; | |
using lhs_rep = typename Lhs::rep; | |
using rhs_rep = typename Rhs::rep; | |
using rep_result = typename _impl::op_result<OperationTag, lhs_rep, rhs_rep>; | |
// width of result archetype is greater of widths of operand archetypes | |
static constexpr _width_type archetype_width = _impl::max(width<typename Lhs::archetype>::value, | |
width<typename Rhs::archetype>::value); | |
using archetype = set_width_t<_impl::make_signed_t<rep_result, policy::is_signed>, archetype_width>; | |
using result_type = elastic_integer<policy::digits, archetype>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_elastic_integer_impl::operate | |
template<class OperationTag, class Lhs, class Rhs> | |
constexpr auto operate(const Lhs& lhs, const Rhs& rhs) | |
-> typename operate_params<OperationTag, Lhs, Rhs>::result_type | |
{ | |
using result_type = typename operate_params<OperationTag, Lhs, Rhs>::result_type; | |
return result_type::from_data( | |
static_cast<typename result_type::rep>(_impl::op_fn<OperationTag>( | |
static_cast<result_type>(lhs).data(), | |
static_cast<result_type>(rhs).data()))); | |
}; | |
} | |
// unary operator- | |
template<int RhsDigits, class RhsArchetype> | |
constexpr auto operator-(const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> elastic_integer<RhsDigits, typename sg14::make_signed<RhsArchetype>::type> | |
{ | |
using result_type = elastic_integer<RhsDigits, typename sg14::make_signed<RhsArchetype>::type>; | |
return result_type::from_data(-static_cast<result_type>(rhs).data()); | |
} | |
// binary operator+ | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype> | |
constexpr auto | |
operator+(const elastic_integer<LhsDigits, LhsArchetype>& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> decltype(_elastic_integer_impl::operate<_impl::add_tag>(lhs, rhs)) | |
{ | |
return _elastic_integer_impl::operate<_impl::add_tag>(lhs, rhs); | |
} | |
// binary operator- | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype> | |
constexpr auto | |
operator-(const elastic_integer<LhsDigits, LhsArchetype>& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> decltype(_elastic_integer_impl::operate<_impl::subtract_tag>(lhs, rhs)) | |
{ | |
return _elastic_integer_impl::operate<_impl::subtract_tag>(lhs, rhs); | |
} | |
// operator* | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype> | |
constexpr auto | |
operator*(const elastic_integer<LhsDigits, LhsArchetype>& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> decltype(_elastic_integer_impl::operate<_impl::multiply_tag>(lhs, rhs)) | |
{ | |
return _elastic_integer_impl::operate<_impl::multiply_tag>(lhs, rhs); | |
} | |
// operator/ | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype> | |
constexpr auto | |
operator/(const elastic_integer<LhsDigits, LhsArchetype>& lhs, const elastic_integer<RhsDigits, RhsArchetype>& rhs) | |
-> decltype(_elastic_integer_impl::operate<_impl::divide_tag>(lhs, rhs)) | |
{ | |
return _elastic_integer_impl::operate<_impl::divide_tag>(lhs, rhs); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// traits | |
template<int Digits, class Archetype> | |
struct make_signed<elastic_integer<Digits, Archetype>> { | |
using type = elastic_integer<Digits, typename make_signed<Archetype>::type>; | |
}; | |
template<int Digits, class Archetype> | |
struct make_unsigned<elastic_integer<Digits, Archetype>> { | |
using type = elastic_integer<Digits, typename make_unsigned<Archetype>::type>; | |
}; | |
template<int Digits, class Archetype> | |
struct width<elastic_integer<Digits, Archetype>> | |
: std::integral_constant<_width_type, elastic_integer<Digits, Archetype>::width> { | |
}; | |
template<int Digits, class Archetype, _width_type MinNumBits> | |
struct set_width<elastic_integer<Digits, Archetype>, MinNumBits> { | |
using type = elastic_integer<MinNumBits - std::numeric_limits<Archetype>::is_signed, Archetype>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::scale<elastic_integer> | |
template<int Digits, class Archetype> | |
struct scale<elastic_integer<Digits, Archetype>> { | |
using Integer = elastic_integer<Digits, Archetype>; | |
constexpr Integer operator()(const Integer& i, int base, int exp) const { | |
return Integer{scale<typename Integer::rep>()(i.data(), base, exp)}; | |
} | |
}; | |
} | |
namespace std { | |
template<int LhsDigits, class LhsArchetype, int RhsDigits, class RhsArchetype> | |
struct common_type<sg14::elastic_integer<LhsDigits, LhsArchetype>, sg14::elastic_integer<RhsDigits, RhsArchetype>> { | |
using type = typename std::conditional<RhsDigits | |
<LhsDigits, sg14::elastic_integer<LhsDigits, LhsArchetype>, sg14::elastic_integer<RhsDigits, RhsArchetype>>::type; | |
}; | |
template<int LhsDigits, class LhsArchetype, class Rhs> | |
struct common_type<sg14::elastic_integer<LhsDigits, LhsArchetype>, Rhs> | |
: common_type<sg14::elastic_integer<LhsDigits, LhsArchetype>, sg14::elastic_integer<std::numeric_limits<Rhs>::digits, Rhs>> { | |
}; | |
template<class Lhs, int RhsDigits, class RhsArchetype> | |
struct common_type<Lhs, sg14::elastic_integer<RhsDigits, RhsArchetype>> | |
: common_type<sg14::elastic_integer<std::numeric_limits<Lhs>::digits, Lhs>, sg14::elastic_integer<RhsDigits, RhsArchetype>> { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// std::numeric_limits for sg14::elastic_integer | |
// note: some members are guessed, | |
// some are temporary (assuming rounding style, traps etc.) | |
// and some are undefined | |
template<int Digits, class Archetype> | |
struct numeric_limits<sg14::elastic_integer<Digits, Archetype>> : numeric_limits<Archetype> { | |
// elastic integer-specific helpers | |
using _archetype_numeric_limits = numeric_limits<Archetype>; | |
using _value_type = sg14::elastic_integer<Digits, Archetype>; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
// standard members | |
static constexpr int digits = Digits; | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _value_type::from_data(_rep_numeric_limits::lowest() >> (_rep_numeric_limits::digits-digits)); | |
} | |
static constexpr _value_type min() noexcept | |
{ | |
return _value_type::from_data(1); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _value_type::from_data(_rep_numeric_limits::max() >> (_rep_numeric_limits::digits-digits)); | |
} | |
}; | |
} | |
#endif // SG14_ELASTIC_INTEGER_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief all definitions related to the `sg14::fixed_point` type | |
#if !defined(SG14_FIXED_POINT_H) | |
#define SG14_FIXED_POINT_H 1 | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief definition of `sg14::fixed_point` type | |
#if !defined(SG14_FIXED_POINT_DEF_H) | |
#define SG14_FIXED_POINT_DEF_H 1 | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
// forward declaration | |
template<class Rep = int, int Exponent = 0> | |
class fixed_point; | |
//////////////////////////////////////////////////////////////////////////////// | |
// implementation-specific definitions | |
namespace _impl { | |
namespace fp { | |
template<class From, class To> | |
struct is_implicitly_convertible; | |
template<class FromRep, int FromExponent, class ToRep, int ToExponent> | |
struct is_implicitly_convertible<fixed_point<FromRep, FromExponent>, fixed_point<ToRep, ToExponent>> { | |
using from_limits = std::numeric_limits<FromRep>; | |
static constexpr int from_integer_digits = from_limits::digits+FromExponent; | |
using to_limits = std::numeric_limits<ToRep>; | |
static constexpr int to_integer_digits = to_limits::digits+ToExponent; | |
static constexpr bool value = | |
to_limits::is_signed>=from_limits::is_signed && to_integer_digits>=from_integer_digits | |
&& ToExponent<=FromExponent; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::float_of_same_size | |
template<class T> | |
using float_of_same_size = set_width_t<float, width<T>::value>; | |
} | |
} | |
/// \brief literal real number approximation that uses fixed-point arithmetic | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \tparam Rep the underlying type used to represent the value | |
/// \tparam Exponent the value by which to scale the integer value in order to get the real value | |
/// | |
/// \par Examples | |
/// | |
/// To define a fixed-point value 1 byte in size with a sign bit, 3 integer bits and 4 fractional bits: | |
/// \snippet snippets.cpp define a fixed_point value | |
template<class Rep, int Exponent> | |
class fixed_point { | |
public: | |
//////////////////////////////////////////////////////////////////////////////// | |
// types | |
/// alias to template parameter, \a Rep | |
using rep = Rep; | |
//////////////////////////////////////////////////////////////////////////////// | |
// constants | |
/// value of template parameter, \a Exponent | |
constexpr static int exponent = Exponent; | |
/// number of binary digits this type can represent; | |
/// equivalent to [std::numeric_limits::digits](http://en.cppreference.com/w/cpp/types/numeric_limits/digits) | |
constexpr static int digits = std::numeric_limits<Rep>::digits; | |
/// number of binary digits devoted to integer part of value; | |
/// can be negative for specializations with especially small ranges | |
constexpr static int integer_digits = digits+exponent; | |
/// number of binary digits devoted to fractional part of value; | |
/// can be negative for specializations with especially large ranges | |
constexpr static int fractional_digits = -exponent; | |
//////////////////////////////////////////////////////////////////////////////// | |
// functions | |
private: | |
// constructor taking representation explicitly using operator++(int)-style trick | |
constexpr fixed_point(rep r, int) | |
:_r(r) | |
{ | |
} | |
public: | |
/// default constructor | |
fixed_point() { } | |
/// constructor taking a fixed-point type explicitly | |
template<class FromRep, int FromExponent, typename std::enable_if<!_impl::fp::is_implicitly_convertible<fixed_point<FromRep, FromExponent>, fixed_point>::value, int>::type Dummy = 0> | |
explicit constexpr fixed_point(const fixed_point<FromRep, FromExponent>& rhs) | |
:_r(fixed_point_to_rep(rhs)) | |
{ | |
} | |
/// constructor taking a fixed-point type implicitly | |
template<class FromRep, int FromExponent, typename std::enable_if<_impl::fp::is_implicitly_convertible<fixed_point<FromRep, FromExponent>, fixed_point>::value, int>::type Dummy = 0> | |
constexpr fixed_point(const fixed_point<FromRep, FromExponent>& rhs) | |
: _r(fixed_point_to_rep(rhs)) | |
{ | |
} | |
/// constructor taking an integer type explicitly | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer | |
&& !_impl::fp::is_implicitly_convertible<fixed_point<S>, fixed_point>::value, int>::type Dummy = 0> | |
explicit constexpr fixed_point(S s) | |
: fixed_point(fixed_point<S, 0>::from_data(s)) | |
{ | |
} | |
/// constructor taking an integer type implicitly | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer | |
&& _impl::fp::is_implicitly_convertible<fixed_point<S>, fixed_point>::value, int>::type Dummy = 0> | |
constexpr fixed_point(S s) | |
: fixed_point(fixed_point<S, 0>::from_data(s)) | |
{ | |
} | |
/// constructor taking a floating-point type | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0> | |
explicit constexpr fixed_point(S s) | |
:_r(floating_point_to_rep(s)) | |
{ | |
} | |
/// copy assignment operator taking an integer type | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer | |
&& _impl::fp::is_implicitly_convertible<fixed_point<S>, fixed_point>::value, int>::type Dummy = 0> | |
fixed_point& operator=(S s) | |
{ | |
return operator=(fixed_point<S, 0>::from_data(s)); | |
} | |
/// copy assignment operator taking a floating-point type | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0> | |
fixed_point& operator=(S s) | |
{ | |
_r = floating_point_to_rep(s); | |
return *this; | |
} | |
/// copy assignement operator taking a fixed-point type | |
template<class FromRep, int FromExponent, typename std::enable_if<_impl::fp::is_implicitly_convertible<fixed_point<FromRep, FromExponent>, fixed_point>::value, int>::type Dummy = 0> | |
fixed_point& operator=(const fixed_point<FromRep, FromExponent>& rhs) | |
{ | |
_r = fixed_point_to_rep(rhs); | |
return *this; | |
} | |
/// returns value represented as integral explicitly | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer, int>::type Dummy = 0> | |
explicit constexpr operator S() const | |
{ | |
return rep_to_integral<S>(_r); | |
} | |
/// returns value represented as integral implicitly | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer | |
&& _impl::fp::is_implicitly_convertible<fixed_point, fixed_point<S>>::value, int>::type Dummy = 0> | |
constexpr operator S() const | |
{ | |
return rep_to_integral<S>(_r); | |
} | |
/// returns value represented as floating-point | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0> | |
explicit constexpr operator S() const | |
{ | |
return rep_to_floating_point<S>(_r); | |
} | |
/// returns non-zeroness represented as boolean | |
explicit constexpr operator bool() const | |
{ | |
return _r!=0; | |
} | |
template<class Rhs> | |
fixed_point& operator*=(const Rhs& rhs); | |
template<class Rhs> | |
fixed_point& operator/=(const Rhs& rhs); | |
/// returns internal representation of value | |
constexpr rep data() const | |
{ | |
return _r; | |
} | |
/// creates an instance given the underlying representation value | |
static constexpr fixed_point from_data(rep r) | |
{ | |
return fixed_point(r, 0); | |
} | |
private: | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy = 0> | |
static constexpr S one(); | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer, int>::type Dummy = 0> | |
static constexpr S one(); | |
template<class S> | |
static constexpr S inverse_one(); | |
template<class S> | |
static constexpr S rep_to_integral(rep r); | |
template<class S> | |
static constexpr rep floating_point_to_rep(S s); | |
template<class S> | |
static constexpr S rep_to_floating_point(rep r); | |
template<class FromRep, int FromExponent> | |
static constexpr rep fixed_point_to_rep(const fixed_point<FromRep, FromExponent>& rhs); | |
//////////////////////////////////////////////////////////////////////////////// | |
// variables | |
rep _r; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// general-purpose implementation-specific definitions | |
namespace _impl { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::is_fixed_point | |
template<class T> | |
struct is_fixed_point; | |
template<class T> | |
struct is_fixed_point | |
: public std::false_type { | |
}; | |
template<class Rep, int Exponent> | |
struct is_fixed_point<fixed_point<Rep, Exponent>> | |
: public std::true_type { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::shift_left | |
// performs a shift operation by a fixed number of bits avoiding two pitfalls: | |
// 1) shifting by a negative amount causes undefined behavior | |
// 2) converting between integer types of different sizes can lose significant bits during shift right | |
// Exponent == 0 | |
template<int exp, class Output, class Input> | |
constexpr Output shift_left(Input i) | |
{ | |
using larger = typename std::conditional< | |
width<Input>::value<=width<Output>::value, | |
Output, Input>::type; | |
return (exp>-std::numeric_limits<larger>::digits) | |
? static_cast<Output>(sg14::scale<larger>()(static_cast<larger>(i), 2, exp)) | |
: Output{0}; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// file-local implementation-specific definitions | |
namespace fp { | |
namespace type { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::type::pow2 | |
// returns given power of 2 | |
template<class S, int Exponent, typename std::enable_if<Exponent==0, int>::type Dummy = 0> | |
constexpr S pow2() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return 1; | |
} | |
template<class S, int Exponent, typename std::enable_if< | |
!(Exponent<=0) && (Exponent<8), int>::type Dummy = 0> | |
constexpr S pow2() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return pow2<S, Exponent-1>()*S(2); | |
} | |
template<class S, int Exponent, typename std::enable_if<(Exponent>=8), int>::type Dummy = 0> | |
constexpr S pow2() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return pow2<S, Exponent-8>()*S(256); | |
} | |
template<class S, int Exponent, typename std::enable_if< | |
!(Exponent>=0) && (Exponent>-8), int>::type Dummy = 0> | |
constexpr S pow2() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return pow2<S, Exponent+1>()*S(.5); | |
} | |
template<class S, int Exponent, typename std::enable_if<(Exponent<=-8), int>::type Dummy = 0> | |
constexpr S pow2() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return pow2<S, Exponent+8>()*S(.003906250); | |
} | |
} | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::fixed_point<> member definitions | |
template<class Rep, int Exponent> | |
template<class S, typename std::enable_if<std::is_floating_point<S>::value, int>::type Dummy> | |
constexpr S fixed_point<Rep, Exponent>::one() | |
{ | |
return _impl::fp::type::pow2<S, -exponent>(); | |
} | |
template<class Rep, int Exponent> | |
template<class S, typename std::enable_if<std::numeric_limits<S>::is_integer, int>::type Dummy> | |
constexpr S fixed_point<Rep, Exponent>::one() | |
{ | |
return fixed_point<S, 0>::from_data(1); | |
} | |
template<class Rep, int Exponent> | |
template<class S> | |
constexpr S fixed_point<Rep, Exponent>::inverse_one() | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return _impl::fp::type::pow2<S, exponent>(); | |
} | |
template<class Rep, int Exponent> | |
template<class S> | |
constexpr S fixed_point<Rep, Exponent>::rep_to_integral(rep r) | |
{ | |
static_assert(std::numeric_limits<S>::is_integer, "S must be integral type"); | |
return _impl::shift_left<exponent, S>(r); | |
} | |
template<class Rep, int Exponent> | |
template<class S> | |
constexpr typename fixed_point<Rep, Exponent>::rep fixed_point<Rep, Exponent>::floating_point_to_rep(S s) | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return static_cast<rep>(s*one<S>()); | |
} | |
template<class Rep, int Exponent> | |
template<class S> | |
constexpr S fixed_point<Rep, Exponent>::rep_to_floating_point(rep r) | |
{ | |
static_assert(std::is_floating_point<S>::value, "S must be floating-point type"); | |
return S(r)*inverse_one<S>(); | |
} | |
template<class Rep, int Exponent> | |
template<class FromRep, int FromExponent> | |
constexpr typename fixed_point<Rep, Exponent>::rep fixed_point<Rep, Exponent>::fixed_point_to_rep(const fixed_point<FromRep, FromExponent>& rhs) | |
{ | |
return _impl::shift_left<FromExponent-exponent, rep>(rhs.data()); | |
} | |
} | |
#endif // SG14_FIXED_POINT_DEF_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief definitions of `sg14::make_fixed` and `sg14::make_ufixed` | |
#if !defined(SG14_MAKE_FIXED_H) | |
#define SG14_MAKE_FIXED_H 1 | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
/// \brief specializes \ref fixed_point with the given number of integer and fractional digits | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \tparam IntegerDigits specifies minimum value of @ref fixed_point::integer_digits | |
/// \tparam FractionalDigits specifies the exact value of @ref fixed_point::fractional_digits | |
/// \tparam Archetype hints at the type of @ref fixed_point::rep | |
/// | |
/// \remarks The signage of \a Archetype specifies signage of the resultant fixed-point type. | |
/// \remarks Typical choices for \a Archetype, `signed` and `unsigned`, | |
/// result in a type that uses built-in integers for \a fixed_point::rep. | |
/// \remarks Resultant type is signed by default. | |
/// | |
/// \par Example: | |
/// | |
/// To generate a fixed-point type with a sign bit, 8 fractional bits and at least 7 integer bits: | |
/// \snippet snippets.cpp use make_fixed | |
/// | |
/// \sa make_ufixed | |
template<int IntegerDigits, int FractionalDigits = 0, class Archetype = signed> | |
using make_fixed = fixed_point< | |
set_width_t<Archetype, IntegerDigits+FractionalDigits+std::numeric_limits<Archetype>::is_signed>, | |
-FractionalDigits>; | |
/// \brief specializes \ref fixed_point with the given number of integer and fractional digits; produces an unsigned type | |
/// \headerfile sg14/fixed_point | |
/// | |
/// | |
/// \sa make_fixed | |
template<int IntegerDigits, int FractionalDigits = 0, class Archetype = unsigned> | |
using make_ufixed = make_fixed< | |
IntegerDigits, | |
FractionalDigits, | |
typename make_unsigned<Archetype>::type>; | |
} | |
#endif // SG14_MAKE_FIXED_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief essential named definitions related to the `sg14::fixed_point` type | |
#if !defined(SG14_FIXED_POINT_NAMED_H) | |
#define SG14_FIXED_POINT_NAMED_H 1 | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief essential definitions related to `sg14::fixed_point` arithmetic | |
#if !defined(SG14_FIXED_POINT_ARITHMETIC_H) | |
#define SG14_FIXED_POINT_ARITHMETIC_H 1 | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// implementation-specific definitions | |
namespace _impl { | |
namespace fp { | |
namespace arithmetic { | |
//////////////////////////////////////////////////////////////////////////////// | |
// tags | |
// strategy | |
struct raw_tag; | |
struct lean_tag; // like-for-like interger arithmetic | |
struct wide_tag; // effort is made to widen to accommodate results of multiplication and division | |
//////////////////////////////////////////////////////////////////////////////// | |
// file-local implementation-specific definitions | |
using ::sg14::fixed_point; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::arithmetic::binary_pair | |
template<class LhsRep, int LhsExponent, class RhsRep, int RhsExponent> | |
struct binary_pair_base { | |
using lhs_type = fixed_point<LhsRep, LhsExponent>; | |
using rhs_type = fixed_point<RhsRep, RhsExponent>; | |
}; | |
template<class Lhs, class Rhs> | |
struct binary_pair; | |
template<class LhsRep, int LhsExponent, class RhsRep, int RhsExponent> | |
struct binary_pair<fixed_point<LhsRep, LhsExponent>, fixed_point<RhsRep, RhsExponent>> | |
: binary_pair_base<LhsRep, LhsExponent, RhsRep, RhsExponent> { | |
}; | |
template<class LhsRep, int LhsExponent, class Rhs> | |
struct binary_pair<fixed_point<LhsRep, LhsExponent>, Rhs> | |
: binary_pair_base<LhsRep, LhsExponent, Rhs, 0> { | |
static_assert(std::numeric_limits<Rhs>::is_integer, | |
"named arithmetic functions take only fixed_point and integral types"); | |
}; | |
template<class Lhs, class RhsRep, int RhsExponent> | |
struct binary_pair<Lhs, fixed_point<RhsRep, RhsExponent>> | |
: binary_pair_base<Lhs, 0, RhsRep, RhsExponent> { | |
static_assert(std::numeric_limits<Lhs>::is_integer, | |
"named arithmetic functions take only fixed_point and integral types"); | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::arithmetic::rep_op_exponent | |
template<class OperationTag, class ... Operands> | |
struct rep_op_exponent; | |
template<class Rhs> | |
struct rep_op_exponent<_impl::negate_tag, Rhs> : public std::integral_constant<int, Rhs::exponent> { | |
}; | |
template<class Lhs, class Rhs> | |
struct rep_op_exponent<_impl::add_tag, Lhs, Rhs> : public std::integral_constant<int, _impl::min<int>( | |
Lhs::exponent, | |
Rhs::exponent)> { | |
}; | |
template<class Lhs, class Rhs> | |
struct rep_op_exponent<_impl::subtract_tag, Lhs, Rhs> | |
: public std::integral_constant<int, _impl::min<int>( | |
Lhs::exponent, | |
Rhs::exponent)> { | |
}; | |
template<class Lhs, class Rhs> | |
struct rep_op_exponent<_impl::multiply_tag, Lhs, Rhs> : public std::integral_constant<int, | |
Lhs::exponent+Rhs::exponent> { | |
}; | |
template<class Lhs, class Rhs> | |
struct rep_op_exponent<_impl::divide_tag, Lhs, Rhs> : public std::integral_constant<int, | |
Lhs::exponent-Rhs::exponent> { | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::arithmetic::result | |
template<class PolicyTag, class OperationTag, class Lhs, class Rhs> | |
struct result; | |
// result<raw_tag> | |
template<class OperationTag, class Lhs, class Rhs> | |
struct result<raw_tag, OperationTag, Lhs, Rhs> { | |
using lhs_rep = typename Lhs::rep; | |
using rhs_rep = typename Rhs::rep; | |
using rep_op_result = _impl::op_result<OperationTag, lhs_rep, rhs_rep>; | |
static constexpr int exponent = rep_op_exponent<OperationTag, Lhs, Rhs>::value; | |
using type = fixed_point<rep_op_result, exponent>; | |
}; | |
// result<lean_tag> | |
template<class OperationTag, class Lhs, class Rhs> | |
struct result<lean_tag, OperationTag, Lhs, Rhs> : result<raw_tag, OperationTag, Lhs, Rhs> {}; | |
// result<wide_tag> | |
template<class OperationTag, class Lhs, class Rhs> | |
struct result<wide_tag, OperationTag, Lhs, Rhs> { | |
using lhs_rep = typename Lhs::rep; | |
using rhs_rep = typename Rhs::rep; | |
using rep_op_result = _impl::op_result<OperationTag, lhs_rep, rhs_rep>; | |
// 'Wide' doesn't guarantee avoiding overflow. Adding a single bit to add/subtract results would often lead to double the width being used. | |
static constexpr int sufficient_sign_bits = std::is_signed<rep_op_result>::value; | |
static constexpr int sufficient_integer_digits = _impl::max(Lhs::integer_digits, | |
Rhs::integer_digits); | |
static constexpr int sufficient_fractional_digits = _impl::max(Lhs::fractional_digits, | |
Rhs::fractional_digits); | |
static constexpr _width_type sufficient_width = | |
sufficient_sign_bits+sufficient_integer_digits+sufficient_fractional_digits; | |
static constexpr int result_width = _impl::max(sufficient_width, width<rep_op_result>::value); | |
using rep_type = set_width_t<rep_op_result, result_width>; | |
using type = fixed_point<rep_type, -sufficient_fractional_digits>; | |
}; | |
template<class Lhs, class Rhs> | |
struct result<wide_tag, _impl::multiply_tag, Lhs, Rhs> { | |
using lhs_rep = typename Lhs::rep; | |
using rhs_rep = typename Rhs::rep; | |
using rep_op_result = _impl::op_result<_impl::multiply_tag, lhs_rep, rhs_rep>; | |
static constexpr int digits = Lhs::digits+Rhs::digits; | |
static constexpr bool is_signed = | |
std::numeric_limits<lhs_rep>::is_signed || std::numeric_limits<rhs_rep>::is_signed; | |
static constexpr int width = digits+is_signed; | |
using prewidened_result_rep = _impl::make_signed_t<rep_op_result, is_signed>; | |
using rep_type = set_width_t<prewidened_result_rep, width>; | |
static constexpr int rep_exponent = rep_op_exponent<_impl::multiply_tag, Lhs, Rhs>::value; | |
using type = fixed_point<rep_type, rep_exponent>; | |
}; | |
template<class Lhs, class Rhs> | |
struct result<wide_tag, _impl::divide_tag, Lhs, Rhs> { | |
using lhs_rep = typename Lhs::rep; | |
using rhs_rep = typename Rhs::rep; | |
using rep_op_result = _impl::op_result<_impl::multiply_tag, lhs_rep, rhs_rep>; | |
static constexpr int integer_digits = Lhs::integer_digits+Rhs::fractional_digits; | |
static constexpr int fractional_digits = Lhs::fractional_digits+Rhs::integer_digits; | |
static constexpr int digits = integer_digits+fractional_digits; | |
static constexpr bool is_signed = | |
std::numeric_limits<lhs_rep>::is_signed || std::numeric_limits<rhs_rep>::is_signed; | |
static constexpr int necessary_width = digits+is_signed; | |
static constexpr int promotion_width = width<rep_op_result>::value; | |
static constexpr int width = _impl::max(necessary_width, promotion_width); | |
using prewidened_result_rep = _impl::make_signed_t<rep_op_result, is_signed>; | |
using rep_type = set_width_t<prewidened_result_rep, width>; | |
static constexpr int rep_exponent = -fractional_digits; | |
using type = fixed_point<rep_type, rep_exponent>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::arithmetic::intermediate | |
template<class PolicyTag, class OperationTag, class Lhs, class Rhs> | |
struct intermediate; | |
// bare add / subtract | |
template<class OperationTag, class Lhs, class Rhs> | |
struct intermediate<lean_tag, OperationTag, Lhs, Rhs> { | |
using _result = result<lean_tag, OperationTag, Lhs, Rhs>; | |
using lhs_type = typename _result::type; | |
using rhs_type = lhs_type; | |
}; | |
template<class Lhs, class Rhs> | |
struct intermediate<lean_tag, _impl::multiply_tag, Lhs, Rhs> { | |
using lhs_type = Lhs; | |
using rhs_type = Rhs; | |
}; | |
template<class Lhs, class Rhs> | |
struct intermediate<lean_tag, _impl::divide_tag, Lhs, Rhs> { | |
using lhs_type = Lhs; | |
using rhs_type = Rhs; | |
}; | |
// intermediate - wide | |
// wide - add/subtract | |
template<class OperationTag, class Lhs, class Rhs> | |
struct intermediate<wide_tag, OperationTag, Lhs, Rhs> { | |
using _result = result<wide_tag, OperationTag, Lhs, Rhs>; | |
using lhs_type = typename _result::type; | |
using rhs_type = lhs_type; | |
}; | |
template<class Lhs, class Rhs> | |
struct intermediate<wide_tag, _impl::multiply_tag, Lhs, Rhs> { | |
using _result = result<wide_tag, _impl::multiply_tag, Lhs, Rhs>; | |
using result_rep = typename _result::rep_type; | |
using prewidened_result_rep = typename _result::prewidened_result_rep; | |
// If the 'natural' result of the rep op is wide enough, stick with it. | |
// This ensures that auto-widening rep types (e.g. elastic_integer) don't get widened twice | |
// but types that need a little help (e.g. built-ins) get widened going into the op. | |
using rep_type = typename std::conditional< | |
width<prewidened_result_rep>::value>=_result::width, | |
typename Lhs::rep, result_rep>::type; | |
using lhs_type = fixed_point<rep_type, Lhs::exponent>; | |
using rhs_type = Rhs; | |
}; | |
template<class Lhs, class Rhs> | |
struct intermediate<wide_tag, _impl::divide_tag, Lhs, Rhs> { | |
using wide_result = result<wide_tag, _impl::divide_tag, Lhs, Rhs>; | |
using rep_type = typename wide_result::rep_type; | |
static constexpr int exponent = Lhs::exponent-Rhs::digits; | |
using lhs_type = fixed_point<rep_type, exponent>; | |
using rhs_type = Rhs; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::arithmetic::operate_params | |
template<class PolicyTag, class OperationTag, class Lhs, class Rhs> | |
struct operate_params; | |
template<class PolicyTag, class OperationTag, class Lhs, class Rhs> | |
struct operate_params { | |
using _binary_pair = binary_pair<Lhs, Rhs>; | |
using lhs_type = typename _binary_pair::lhs_type; | |
using rhs_type = typename _binary_pair::rhs_type; | |
using _intermediate = intermediate<PolicyTag, OperationTag, lhs_type, rhs_type>; | |
using intermediate_lhs = typename _intermediate::lhs_type; | |
using intermediate_rhs = typename _intermediate::rhs_type; | |
using _result = result<PolicyTag, OperationTag, lhs_type, rhs_type>; | |
using result_type = typename _result::type; | |
}; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// mappings from named function strategies to public API | |
// strategy aliases - for ease of flip-flopping | |
using arithmetic_operator_tag = arithmetic::lean_tag; | |
using division_arithmetic_operator_tag = arithmetic::wide_tag; | |
using named_function_tag = arithmetic::wide_tag; | |
using division_named_function_tag = arithmetic::lean_tag; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fixed_point::operate | |
template<class PolicyTag, class OperationTag, class Lhs, class Rhs> | |
constexpr auto operate(const Lhs& lhs, const Rhs& rhs) | |
-> typename arithmetic::operate_params<PolicyTag, OperationTag, Lhs, Rhs>::result_type | |
{ | |
using params = arithmetic::operate_params<PolicyTag, OperationTag, Lhs, Rhs>; | |
using intermediate_lhs = typename params::intermediate_lhs; | |
using intermediate_rhs = typename params::intermediate_rhs; | |
using result_type = typename params::result_type; | |
using result_rep = typename result_type::rep; | |
return result_type::from_data( | |
static_cast<result_rep>( | |
_impl::op_fn<OperationTag>( | |
static_cast<intermediate_lhs>(lhs).data(), | |
static_cast<intermediate_rhs>(rhs).data()))); | |
}; | |
} | |
} | |
} | |
#endif // SG14_FIXED_POINT_ARITHMETIC_H | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
/// \brief calculates the negative of a \ref fixed_point value | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param rhs input value | |
/// | |
/// \return negative: - rhs | |
/// | |
/// \note This function negates the value | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa add, subtract, multiply, divide | |
template<class RhsRep, int RhsExponent> | |
constexpr auto negate(const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> fixed_point<decltype(-rhs.data()), RhsExponent> | |
{ | |
using result_type = fixed_point<decltype(-rhs.data()), RhsExponent>; | |
return result_type::from_data(-rhs.data()); | |
} | |
/// \brief calculates the sum of two \ref fixed_point values | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param lhs, rhs augend and addend | |
/// | |
/// \return sum: lhs + rhs | |
/// | |
/// \note This function add the values | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa negate, subtract, multiply, divide | |
template<class Lhs, class Rhs> | |
constexpr auto add(const Lhs& lhs, const Rhs& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag, _impl::add_tag>(lhs, rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::named_function_tag, _impl::add_tag>(lhs, rhs); | |
} | |
/// \brief calculates the difference of two \ref fixed_point values | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param lhs, rhs minuend and subtrahend | |
/// | |
/// \return difference: lhs - rhs | |
/// | |
/// \note This function subtracts the values | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa negate, add, multiply, divide | |
template<class Lhs, class Rhs> | |
constexpr auto subtract(const Lhs& lhs, const Rhs& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag, _impl::subtract_tag>(lhs, | |
rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::named_function_tag, _impl::subtract_tag>(lhs, | |
rhs); | |
} | |
/// \brief calculates the product of two \ref fixed_point factors | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param lhs, rhs the factors | |
/// | |
/// \return product: lhs * rhs | |
/// | |
/// \note This function multiplies the values | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa negate, add, subtract, divide | |
template<class Lhs, class Rhs> | |
constexpr auto multiply(const Lhs& lhs, const Rhs& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag, _impl::multiply_tag>(lhs, rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::named_function_tag, _impl::multiply_tag>(lhs, rhs); | |
} | |
/// \brief calculates the quotient of two \ref fixed_point values | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param lhs, rhs dividend and divisor | |
/// | |
/// \return quotient: lhs / rhs | |
/// | |
/// \note This function divides the values | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa negate, add, subtract, multiply | |
template<class Lhs, class Rhs> | |
constexpr auto divide(const Lhs& lhs, const Rhs& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::division_named_function_tag, _impl::divide_tag>(lhs, | |
rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::division_named_function_tag, _impl::divide_tag>(lhs, | |
rhs); | |
} | |
} | |
#endif // SG14_FIXED_POINT_NAMED_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief `sg14::fixed_point` specializations of \c std::common_type | |
#if !defined(SG14_FIXED_POINT_COMMON_TYPE_H) | |
#define SG14_FIXED_POINT_COMMON_TYPE_H 1 | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// implementation-specific definitions | |
namespace _impl { | |
namespace fp { | |
namespace ct { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::_impl::fp::ct::common_type_mixed | |
template<class Lhs, class Rhs, class _Enable = void> | |
struct common_type_mixed; | |
// given a fixed-point and an integer type, | |
// generates a fixed-point type that is as big as both of them (or as close as possible) | |
template<class LhsRep, int LhsExponent, class RhsInteger> | |
struct common_type_mixed<fixed_point | |
<LhsRep, LhsExponent>, RhsInteger, typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer>::type> : std::common_type< | |
fixed_point<LhsRep, LhsExponent>, fixed_point<RhsInteger, 0>> { | |
}; | |
// given a fixed-point and a floating-point type, | |
// generates a floating-point type that is as big as both of them (or as close as possible) | |
template<class LhsRep, int LhsExponent, class Float> | |
struct common_type_mixed< | |
fixed_point<LhsRep, LhsExponent>, | |
Float, | |
typename std::enable_if<std::is_floating_point<Float>::value>::type> : std::common_type<_impl::fp::float_of_same_size<LhsRep>, Float> { | |
}; | |
} | |
} | |
} | |
} | |
namespace std { | |
//////////////////////////////////////////////////////////////////////////////// | |
// std::common_type<> specializations related to sg14::sg14::fixed_point<> | |
/// \brief Produce a fixed-point type with the given number of integer and fractional digits. | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \tparam IntegerDigits specifies minimum value of @ref sg14::fixed_point::integer_digits | |
/// \tparam FractionalDigits specifies the exact value of @ref sg14::fixed_point::fractional_digits | |
/// \tparam Archetype hints at the type of @ref sg14::fixed_point::rep | |
/// | |
/// \remarks The signage of \a Archetype specifies signage of the resultant fixed-point type. | |
/// \remarks Typical choices for \a Archetype, `signed` and `unsigned`, | |
/// result in a type that uses built-in integers for \a fixed_point::rep. | |
/// \remarks Resultant type is signed by default. | |
/// | |
/// \par Example: | |
/// | |
/// To generate a fixed-point type with a sign bit, 8 fractional bits and at least 7 integer bits: | |
/// \snippet snippets.cpp use make_fixed | |
/// | |
/// \sa make_ufixed | |
template<class Rep, int Exponent> | |
struct common_type<sg14::fixed_point<Rep, Exponent>> { | |
using type = sg14::fixed_point< | |
typename std::common_type<Rep>::type, | |
Exponent>; | |
}; | |
// std::common_type<fixed_point<>, not-fixed-point> | |
template<class LhsRep, int LhsExponent, class Rhs> | |
struct common_type<sg14::fixed_point<LhsRep, LhsExponent>, Rhs> { | |
static_assert(!sg14::_impl::is_fixed_point<Rhs>::value, "fixed-point Rhs type"); | |
using type = typename sg14::_impl::fp::ct::common_type_mixed<sg14::fixed_point<LhsRep, LhsExponent>, Rhs>::type; | |
}; | |
// std::common_type<not-fixed-point, fixed_point<>> | |
template<class Lhs, class RhsRep, int RhsExponent> | |
struct common_type<Lhs, sg14::fixed_point<RhsRep, RhsExponent>> { | |
static_assert(!sg14::_impl::is_fixed_point<Lhs>::value, "fixed-point Lhs type"); | |
using type = typename sg14::_impl::fp::ct::common_type_mixed<sg14::fixed_point<RhsRep, RhsExponent>, Lhs>::type; | |
}; | |
// std::common_type<fixed_point<>, fixed_point<>> | |
template<class LhsRep, int LhsExponent, class RhsRep, int RhsExponent> | |
struct common_type<sg14::fixed_point<LhsRep, LhsExponent>, sg14::fixed_point<RhsRep, RhsExponent>> { | |
using _result_rep = typename std::common_type<LhsRep, RhsRep>::type; | |
// exponent is the lower of the two operands' unless that could cause overflow in which case it is adjusted downward | |
static constexpr int _capacity = std::numeric_limits<_result_rep>::digits; | |
static constexpr int _ideal_max_top = sg14::_impl::max( | |
sg14::fixed_point<LhsRep, LhsExponent>::integer_digits, | |
sg14::fixed_point<RhsRep, RhsExponent>::integer_digits); | |
static constexpr int _ideal_exponent = sg14::_impl::min(LhsExponent, RhsExponent); | |
static constexpr int _exponent = ((_ideal_max_top-_ideal_exponent)<=_capacity) ? _ideal_exponent : | |
_ideal_max_top-_capacity; | |
using type = sg14::fixed_point<_result_rep, _exponent>; | |
}; | |
} | |
#endif // SG14_FIXED_POINT_COMMON_TYPE_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief `sg14::fixed_point` operators | |
#if !defined(SG14_FIXED_POINT_OPERATORS_H) | |
#define SG14_FIXED_POINT_OPERATORS_H 1 | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// (fixed_point @ fixed_point) comparison operators | |
template<class Rep, int Exponent> | |
constexpr bool operator==( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()==rhs.data(); | |
} | |
template<class Rep, int Exponent> | |
constexpr bool operator!=( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()!=rhs.data(); | |
} | |
template<class Rep, int Exponent> | |
constexpr bool operator<( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()<rhs.data(); | |
} | |
template<class Rep, int Exponent> | |
constexpr bool operator>( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()>rhs.data(); | |
} | |
template<class Rep, int Exponent> | |
constexpr bool operator>=( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()>=rhs.data(); | |
} | |
template<class Rep, int Exponent> | |
constexpr bool operator<=( | |
const fixed_point<Rep, Exponent>& lhs, | |
const fixed_point<Rep, Exponent>& rhs) | |
{ | |
return lhs.data()<=rhs.data(); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// (fixed_point @ fixed_point) arithmetic operators | |
// negate | |
template<class RhsRep, int RhsExponent> | |
constexpr auto operator-(const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> fixed_point<decltype(-rhs.data()), RhsExponent> | |
{ | |
using result_type = fixed_point<decltype(-rhs.data()), RhsExponent>; | |
return result_type::from_data(-rhs.data()); | |
} | |
// add | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsRep, int RhsExponent> | |
constexpr auto operator+( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::add_tag>(lhs, | |
rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::add_tag>(lhs, | |
rhs); | |
} | |
// subtract | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsRep, int RhsExponent> | |
constexpr auto operator-( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::subtract_tag>( | |
lhs, rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::subtract_tag>( | |
lhs, rhs); | |
} | |
// multiply | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsRep, int RhsExponent> | |
constexpr auto operator*( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::multiply_tag>( | |
lhs, rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag, _impl::multiply_tag>( | |
lhs, rhs); | |
} | |
// divide | |
template<class LhsRep, int LhsExponent, class RhsRep, int RhsExponent> | |
constexpr auto operator/( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(_impl::fp::operate<_impl::fp::division_arithmetic_operator_tag, _impl::divide_tag>( | |
lhs, rhs)) | |
{ | |
return _impl::fp::operate<_impl::fp::division_arithmetic_operator_tag, _impl::divide_tag>( | |
lhs, rhs); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// heterogeneous operator overloads | |
// | |
// compare two objects of different fixed_point specializations | |
template<class Lhs, class Rhs> | |
constexpr auto operator==(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)==static_cast<common_type>(rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator!=(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)!=static_cast<common_type>(rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator<(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)<static_cast<common_type>(rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator>(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)>static_cast<common_type>(rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator>=(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)>=static_cast<common_type>(rhs); | |
} | |
template<class Lhs, class Rhs> | |
constexpr auto operator<=(const Lhs& lhs, const Rhs& rhs) | |
-> typename std::enable_if< | |
_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value, bool>::type | |
{ | |
using common_type = _impl::common_type_t<Lhs, Rhs>; | |
return static_cast<common_type>(lhs)<=static_cast<common_type>(rhs); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// (fixed_point @ non-fixed_point) arithmetic operators | |
// fixed-point, integer -> fixed-point | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsInteger, | |
typename = typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer>::type> | |
constexpr auto operator+(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsInteger& rhs) | |
-> decltype(lhs + fixed_point<RhsInteger, 0>{rhs}) | |
{ | |
return lhs + fixed_point<RhsInteger, 0>{rhs}; | |
} | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsInteger, | |
typename = typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer>::type> | |
constexpr auto operator-(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsInteger& rhs) | |
-> decltype(lhs - fixed_point<RhsInteger, 0>{rhs}) | |
{ | |
return lhs - fixed_point<RhsInteger, 0>{rhs}; | |
} | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsInteger, | |
typename = typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer>::type> | |
constexpr auto operator*(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsInteger& rhs) | |
-> decltype(lhs*fixed_point<RhsInteger>(rhs)) | |
{ | |
return lhs*fixed_point<RhsInteger>(rhs); | |
} | |
template< | |
class LhsRep, int LhsExponent, | |
class RhsInteger, | |
typename = typename std::enable_if<std::numeric_limits<RhsInteger>::is_integer>::type> | |
constexpr auto operator/(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsInteger& rhs) | |
-> decltype(lhs/fixed_point<RhsInteger>{rhs}) | |
{ | |
return lhs/fixed_point<RhsInteger>{rhs}; | |
} | |
// integer. fixed-point -> fixed-point | |
template< | |
class LhsInteger, | |
class RhsRep, int RhsExponent, | |
typename = typename std::enable_if<std::numeric_limits<LhsInteger>::is_integer>::type> | |
constexpr auto operator+(const LhsInteger& lhs, const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(fixed_point<LhsInteger, 0>{lhs} + rhs) | |
{ | |
return fixed_point<LhsInteger, 0>{lhs} + rhs; | |
} | |
template< | |
class LhsInteger, | |
class RhsRep, int RhsExponent, | |
typename = typename std::enable_if<std::numeric_limits<LhsInteger>::is_integer>::type> | |
constexpr auto operator-(const LhsInteger& lhs, const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(fixed_point<LhsInteger>{lhs}-rhs) | |
{ | |
return fixed_point<LhsInteger>{lhs}-rhs; | |
} | |
template< | |
class LhsInteger, | |
class RhsRep, int RhsExponent, | |
typename = typename std::enable_if<std::numeric_limits<LhsInteger>::is_integer>::type> | |
constexpr auto operator*(const LhsInteger& lhs, const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(fixed_point<LhsInteger>{lhs}*rhs) | |
{ | |
return fixed_point<LhsInteger>{lhs}*rhs; | |
} | |
template< | |
class LhsInteger, | |
class RhsRep, int RhsExponent, | |
typename = typename std::enable_if<std::numeric_limits<LhsInteger>::is_integer>::type> | |
constexpr auto operator/(const LhsInteger& lhs, const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> decltype(fixed_point<LhsInteger>{lhs}/rhs) | |
{ | |
return fixed_point<LhsInteger>{lhs}/rhs; | |
} | |
// fixed-point, floating-point -> floating-point | |
template<class LhsRep, int LhsExponent, class RhsFloat, typename = typename std::enable_if<std::is_floating_point<RhsFloat>::value>::type> | |
constexpr auto operator+(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsFloat& rhs)-> _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat>; | |
return static_cast<result_type>(lhs)+static_cast<result_type>(rhs); | |
} | |
template<class LhsRep, int LhsExponent, class RhsFloat, typename = typename std::enable_if<std::is_floating_point<RhsFloat>::value>::type> | |
constexpr auto operator-(const fixed_point<LhsRep, LhsExponent>& lhs, const RhsFloat& rhs)-> _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat>; | |
return static_cast<result_type>(lhs)-static_cast<result_type>(rhs); | |
} | |
template<class LhsRep, int LhsExponent, class RhsFloat> | |
constexpr auto operator*( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const RhsFloat& rhs) | |
-> _impl::common_type_t< | |
fixed_point<LhsRep, LhsExponent>, | |
typename std::enable_if<std::is_floating_point<RhsFloat>::value, RhsFloat>::type> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat>; | |
return static_cast<result_type>(lhs)*rhs; | |
} | |
template<class LhsRep, int LhsExponent, class RhsFloat> | |
constexpr auto operator/( | |
const fixed_point<LhsRep, LhsExponent>& lhs, | |
const RhsFloat& rhs) | |
-> _impl::common_type_t< | |
fixed_point<LhsRep, LhsExponent>, | |
typename std::enable_if<std::is_floating_point<RhsFloat>::value, RhsFloat>::type> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat>; | |
return static_cast<result_type>(lhs)/rhs; | |
} | |
// floating-point, fixed-point -> floating-point | |
template<class LhsFloat, class RhsRep, int RhsExponent, typename = typename std::enable_if<std::is_floating_point<LhsFloat>::value>::type> | |
constexpr auto operator+(const LhsFloat& lhs, const fixed_point<RhsRep, RhsExponent>& rhs)-> _impl::common_type_t<LhsFloat, fixed_point<RhsRep, RhsExponent>> | |
{ | |
using result_type = _impl::common_type_t<LhsFloat, fixed_point<RhsRep, RhsExponent>>; | |
return static_cast<result_type>(lhs)+static_cast<result_type>(rhs); | |
} | |
template<class LhsFloat, class RhsRep, int RhsExponent, typename = typename std::enable_if<std::is_floating_point<LhsFloat>::value>::type> | |
constexpr auto operator-(const LhsFloat& lhs, const fixed_point<RhsRep, RhsExponent>& rhs)-> _impl::common_type_t<LhsFloat, fixed_point<RhsRep, RhsExponent>> | |
{ | |
using result_type = _impl::common_type_t<LhsFloat, fixed_point<RhsRep, RhsExponent>>; | |
return static_cast<result_type>(lhs)-static_cast<result_type>(rhs); | |
} | |
template<class LhsFloat, class RhsRep, int RhsExponent> | |
constexpr auto operator*( | |
const LhsFloat& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> _impl::common_type_t< | |
typename std::enable_if<std::is_floating_point<LhsFloat>::value, LhsFloat>::type, | |
fixed_point<RhsRep, RhsExponent>> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<RhsRep, RhsExponent>, LhsFloat>; | |
return lhs*static_cast<result_type>(rhs); | |
} | |
template<class LhsFloat, class RhsRep, int RhsExponent> | |
constexpr auto operator/( | |
const LhsFloat& lhs, | |
const fixed_point<RhsRep, RhsExponent>& rhs) | |
-> _impl::common_type_t< | |
typename std::enable_if<std::is_floating_point<LhsFloat>::value, LhsFloat>::type, | |
fixed_point<RhsRep, RhsExponent>> | |
{ | |
using result_type = _impl::common_type_t<fixed_point<RhsRep, RhsExponent>, LhsFloat>; | |
return lhs/ | |
static_cast<result_type>(rhs); | |
} | |
template<class LhsRep, int Exponent, class Rhs> | |
fixed_point<LhsRep, Exponent>& operator+=(fixed_point<LhsRep, Exponent>& lhs, const Rhs& rhs) | |
{ | |
return lhs = static_cast<fixed_point<LhsRep, Exponent>>(lhs+rhs); | |
} | |
template<class LhsRep, int Exponent, class Rhs> | |
fixed_point<LhsRep, Exponent>& operator-=(fixed_point<LhsRep, Exponent>& lhs, const Rhs& rhs) | |
{ | |
return lhs = static_cast<fixed_point<LhsRep, Exponent>>(lhs-rhs); | |
} | |
template<class LhsRep, int Exponent> | |
template<class Rhs> | |
fixed_point<LhsRep, Exponent>& | |
fixed_point<LhsRep, Exponent>::operator*=(const Rhs& rhs) | |
{ | |
_r *= static_cast<rep>(rhs); | |
return *this; | |
} | |
template<class LhsRep, int Exponent> | |
template<class Rhs> | |
fixed_point<LhsRep, Exponent>& | |
fixed_point<LhsRep, Exponent>::operator/=(const Rhs& rhs) | |
{ | |
_r /= static_cast<rep>(rhs); | |
return *this; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// shift operators | |
template<class LhsRep, int LhsExponent, class Rhs> | |
constexpr fixed_point<LhsRep, LhsExponent> | |
operator<<(const fixed_point<LhsRep, LhsExponent>& lhs, const Rhs& rhs) | |
{ | |
return fixed_point<LhsRep, LhsExponent>::from_data(lhs.data() << rhs); | |
}; | |
template<class LhsRep, int LhsExponent, class Rhs> | |
constexpr fixed_point<LhsRep, LhsExponent> | |
operator>>(const fixed_point<LhsRep, LhsExponent>& lhs, const Rhs& rhs) | |
{ | |
return fixed_point<LhsRep, LhsExponent>::from_data(lhs.data() >> rhs); | |
}; | |
} | |
#endif // SG14_FIXED_POINT_OPERATORS_H | |
// Copyright John McFarlane 2015 - 2016. | |
// Distributed under the Boost Software License, Version 1.0. | |
// (See accompanying file ../LICENSE_1_0.txt or copy at | |
// http://www.boost.org/LICENSE_1_0.txt) | |
/// \file | |
/// \brief supplemental definitions related to the `sg14::fixed_point` type; | |
/// definitions that straddle two homes, e.g. fixed_point and cmath, traits or limits; | |
/// included from sg14/fixed_point - do not include directly! | |
#if !defined(SG14_FIXED_POINT_EXTRAS_H) | |
#define SG14_FIXED_POINT_EXTRAS_H 1 | |
#include <cmath> | |
#include <istream> | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::abs | |
template<class Rep, int Exponent> | |
constexpr auto abs(const fixed_point<Rep, Exponent>& x) noexcept | |
-> decltype(-x) | |
{ | |
return (x >= 0) ? x : -x; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::sqrt helper functions | |
namespace _impl { | |
namespace fp { | |
namespace extras { | |
template<class Rep> | |
constexpr Rep sqrt_bit( | |
Rep n, | |
Rep bit = Rep(1) << (width<Rep>::value-2)) | |
{ | |
return (bit>n) ? sqrt_bit<Rep>(n, bit >> 2) : bit; | |
} | |
template<class Rep> | |
constexpr Rep sqrt_solve3( | |
Rep n, | |
Rep bit, | |
Rep result) | |
{ | |
return (bit!=Rep{0}) | |
? (n>=result+bit) | |
? sqrt_solve3<Rep>( | |
static_cast<Rep>(n-(result+bit)), | |
bit >> 2, | |
static_cast<Rep>((result >> 1)+bit)) | |
: sqrt_solve3<Rep>(n, bit >> 2, result >> 1) | |
: result; | |
} | |
template<class Rep> | |
constexpr Rep sqrt_solve1(Rep n) | |
{ | |
return sqrt_solve3<Rep>(n, sqrt_bit<Rep>(n), Rep{0}); | |
} | |
} | |
} | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::sqrt | |
/// \brief calculates the square root of a \ref fixed_point value | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \param x input parameter | |
/// | |
/// \return square root of x | |
/// | |
/// \note This function is a placeholder implementation with poor run-time performance characteristics. | |
/// \note It uses | |
/// divides the values | |
/// without performing any additional scaling or conversion. | |
/// | |
/// \sa negate, add, subtract, multiply | |
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_.28base_2.29 | |
// ? | |
template<class Rep, int Exponent> | |
constexpr fixed_point <Rep, Exponent> | |
sqrt(const fixed_point <Rep, Exponent>& x) | |
{ | |
using widened_type = fixed_point<set_width_t<Rep, width<Rep>::value*2>, Exponent*2>; | |
return | |
#if defined(SG14_EXCEPTIONS_ENABLED) | |
(x<fixed_point<Rep, Exponent>(0)) | |
? throw std::invalid_argument("cannot represent square root of negative value") : | |
#endif | |
fixed_point<Rep, Exponent>::from_data( | |
static_cast<Rep>(_impl::fp::extras::sqrt_solve1(widened_type{x}.data()))); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::trig | |
// | |
// Placeholder implementations fall back on <cmath> functions which is slow | |
// due to conversion to and from floating-point types; also inconvenient as | |
// many <cmath> functions are not constexpr. | |
namespace _impl { | |
namespace fp { | |
namespace extras { | |
template<class Rep, int Exponent, _impl::fp::float_of_same_size<Rep>(* F)( | |
_impl::fp::float_of_same_size<Rep>)> | |
constexpr fixed_point <Rep, Exponent> | |
crib(const fixed_point <Rep, Exponent>& x) noexcept | |
{ | |
using floating_point = _impl::fp::float_of_same_size<Rep>; | |
return static_cast<fixed_point<Rep, Exponent>>(F(static_cast<floating_point>(x))); | |
} | |
} | |
} | |
} | |
template<class Rep, int Exponent> | |
constexpr fixed_point <Rep, Exponent> | |
sin(const fixed_point <Rep, Exponent>& x) noexcept | |
{ | |
return _impl::fp::extras::crib<Rep, Exponent, std::sin>(x); | |
} | |
template<class Rep, int Exponent> | |
constexpr fixed_point <Rep, Exponent> | |
cos(const fixed_point <Rep, Exponent>& x) noexcept | |
{ | |
return _impl::fp::extras::crib<Rep, Exponent, std::cos>(x); | |
} | |
template<class Rep, int Exponent> | |
constexpr fixed_point <Rep, Exponent> | |
exp(const fixed_point <Rep, Exponent>& x) noexcept | |
{ | |
return _impl::fp::extras::crib<Rep, Exponent, std::exp>(x); | |
} | |
template<class Rep, int Exponent> | |
constexpr fixed_point <Rep, Exponent> | |
pow(const fixed_point <Rep, Exponent>& x) noexcept | |
{ | |
return _impl::fp::extras::crib<Rep, Exponent, std::pow>(x); | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::make_signed<fixed_point<>> | |
template<class Rep, int Exponent> | |
struct make_signed<fixed_point<Rep, Exponent> > { | |
using type = fixed_point<typename sg14::make_signed<Rep>::type, Exponent>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::make_unsigned<fixed_point<>> | |
template<class Rep, int Exponent> | |
struct make_unsigned<fixed_point<Rep, Exponent>> { | |
using type = fixed_point<typename sg14::make_unsigned<Rep>::type, Exponent>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::fixed_point type trait specializations | |
/// determines the width of given fixed_point type | |
/// | |
/// \tparam Rep the \a Rep parameter of @ref fixed_point | |
/// \tparam Exponent the \a Exponent parameter of @ref fixed_point | |
/// | |
/// \sa set_width | |
template<class Rep, int Exponent> | |
struct width<fixed_point<Rep, Exponent>> { | |
/// width of the given fixed_point type | |
static constexpr _width_type value = width<Rep>::value; | |
}; | |
/// \brief produces equivalent fixed-point type at a new width | |
/// \headerfile sg14/fixed_point | |
/// | |
/// \tparam Rep the \a Rep parameter of @ref fixed_point | |
/// \tparam Exponent the \a Exponent parameter of @ref fixed_point | |
/// \tparam MinNumBits the desired size of the resultant type such that <tt>(sg14::width<fixed_point<Rep, Exponent>>\::value >= MinNumBytes)</tt> | |
/// | |
/// \sa width | |
template<class Rep, int Exponent, _width_type MinNumBits> | |
struct set_width<fixed_point<Rep, Exponent>, MinNumBits> { | |
/// resultant type; a fixed_point specialization that is at least \a MinNumBits bytes in width | |
using type = fixed_point<set_width_t<Rep, MinNumBits>, Exponent>; | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::fixed_point streaming - (placeholder implementation) | |
template<class Rep, int Exponent> | |
::std::ostream& operator<<(::std::ostream& out, const fixed_point <Rep, Exponent>& fp) | |
{ | |
return out << static_cast<long double>(fp); | |
} | |
template<class Rep, int Exponent> | |
::std::istream& operator>>(::std::istream& in, fixed_point <Rep, Exponent>& fp) | |
{ | |
long double ld; | |
in >> ld; | |
fp = ld; | |
return in; | |
} | |
} | |
namespace std { | |
//////////////////////////////////////////////////////////////////////////////// | |
// std::numeric_limits for sg14::fixed_point | |
// note: some members are guessed, | |
// some are temporary (assuming rounding style, traps etc.) | |
// and some are undefined | |
template<class Rep, int Exponent> | |
struct numeric_limits<sg14::fixed_point<Rep, Exponent>> : public std::numeric_limits<Rep> { | |
// fixed-point-specific helpers | |
using _value_type = sg14::fixed_point<Rep, Exponent>; | |
using _rep = typename _value_type::rep; | |
using _rep_numeric_limits = numeric_limits<_rep>; | |
// standard members | |
static constexpr _value_type min() noexcept | |
{ | |
return _value_type::from_data(sg14::_impl::min(_rep{1}, _rep_numeric_limits::max())); | |
} | |
static constexpr _value_type max() noexcept | |
{ | |
return _value_type::from_data(_rep_numeric_limits::max()); | |
} | |
static constexpr _value_type lowest() noexcept | |
{ | |
return _value_type::from_data(_rep_numeric_limits::lowest()); | |
} | |
static constexpr bool is_integer = false; | |
static constexpr _value_type epsilon() noexcept | |
{ | |
return _value_type::from_data(1); | |
} | |
static constexpr _value_type round_error() noexcept | |
{ | |
return static_cast<_value_type>(0); | |
} | |
static constexpr _value_type infinity() noexcept | |
{ | |
return static_cast<_value_type>(0); | |
} | |
static constexpr _value_type quiet_NaN() noexcept | |
{ | |
return static_cast<_value_type>(0); | |
} | |
static constexpr _value_type signaling_NaN() noexcept | |
{ | |
return static_cast<_value_type>(0); | |
} | |
static constexpr _value_type denorm_min() noexcept | |
{ | |
return static_cast<_value_type>(0); | |
} | |
}; | |
} | |
#endif // SG14_FIXED_POINT_EXTRAS_H | |
#endif // SG14_FIXED_POINT_H | |
/// study group 14 of the C++ working group | |
namespace sg14 { | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14-specific definitions | |
/// \brief literal real number approximation that uses fixed-point arithmetic and auto-widens to avoid overflow | |
/// | |
/// \tparam IntegerDigits the number of inteteger digits that can be stored | |
/// \tparam FractionalDigits the number of fractional digits that can be stored | |
/// \tparam Archetype the kind of integer type to use to represent values | |
/// | |
/// \sa elastic_integer | |
template<int IntegerDigits, int FractionalDigits = 0, class Archetype = signed> | |
using elastic = fixed_point<elastic_integer<IntegerDigits+FractionalDigits, Archetype>, -FractionalDigits>; | |
//////////////////////////////////////////////////////////////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// sg14::elasticate | |
// sg14::elasticate helper definitions | |
namespace _elastic_impl { | |
template<class Integer> | |
constexpr int num_integer_bits(Integer value) | |
{ | |
return value ? 1+num_integer_bits(value/2) : 0; | |
} | |
template<class Integer> | |
constexpr int num_fractional_bits(Integer value) | |
{ | |
return (((value/2)*2)==value) ? num_fractional_bits(value/2)-1 : 0; | |
} | |
template<class Integer, Integer Value, class Archetype, class Enabled = void> | |
struct elastication; | |
template<class Integer, Integer Value, class Archetype> | |
struct elastication<Integer, Value, Archetype, typename std::enable_if<Value==0>::type> { | |
using type = elastic<1, 0, typename make_unsigned<Archetype>::type>; | |
}; | |
template<class Integer, Integer Value, class Archetype> | |
struct elastication<Integer, Value, Archetype, typename std::enable_if<Value!=0>::type> { | |
static_assert(std::is_integral<Integer>::value, "template parameter, Integer, is not integral"); | |
using archetype = typename std::conditional<(Value>=0), | |
typename make_unsigned<Archetype>::type, | |
typename make_signed<Archetype>::type>::type; | |
using type = elastic< | |
sg14::_impl::max(1, num_integer_bits(Value)), | |
num_fractional_bits(Value), | |
archetype>; | |
}; | |
template<class Integer, Integer Value, class Archetype> | |
using elasticate_t = typename _elastic_impl::elastication<Integer, Value, Archetype>::type; | |
} | |
/// \brief generate an \ref elastic object of given value | |
/// | |
/// \tparam Value the integer number to be represented | |
/// \tparam Archetype the archetype of the resultant \ref elastic object | |
/// | |
/// \return the given value represented using an \ref elastic type | |
/// | |
/// \note The return type is guaranteed to be no larger than is necessary to represent the value. | |
/// | |
/// \par Example | |
/// | |
/// To define a 1-byte object with value 1024: | |
/// \snippet snippets.cpp define a small object using elasticate | |
/// | |
/// To define a int-sized object with value 1024: | |
/// \snippet snippets.cpp define a fast object using elasticate | |
template<std::int64_t Value, class Archetype = int> | |
constexpr auto elasticate() | |
-> _elastic_impl::elasticate_t<std::int64_t, Value, Archetype> | |
{ | |
return _elastic_impl::elasticate_t<std::int64_t, Value, Archetype>{Value}; | |
} | |
} | |
#endif // SG14_ELASTIC_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment