Skip to content

Instantly share code, notes, and snippets.

@johnmcfarlane
Last active November 15, 2016 04:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnmcfarlane/d3adca0da5b5a06d87d60475fe063275 to your computer and use it in GitHub Desktop.
Save johnmcfarlane/d3adca0da5b5a06d87d60475fe063275 to your computer and use it in GitHub Desktop.
single-header version of header, elastic.h, from johnmcfarlane/fixed_point
// 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