Skip to content

Instantly share code, notes, and snippets.

@johnmcfarlane
Last active June 28, 2017 14:47
Show Gist options
  • Save johnmcfarlane/23b8bc5fefb77d482306c5bc837b5df1 to your computer and use it in GitHub Desktop.
Save johnmcfarlane/23b8bc5fefb77d482306c5bc837b5df1 to your computer and use it in GitHub Desktop.
Single-header version of fixed_point library
// Copyright John McFarlane 2017.
// 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)
// mechanically retrieved, single-header version of fixed_point library
// https://github.com/johnmcfarlane/fixed_point
#if ! defined(SG14_FIXED_POINT_SINGLE_HEADER)
#define SG14_FIXED_POINT_SINGLE_HEADER
#include <cmath>
#include <utility>
#include <istream>
#include <climits>
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <type_traits>
namespace sg14 {
namespace _impl {
template<class ... T>
using common_type_t = typename std::common_type<T ...>::type;
template<bool C, class ... T>
using enable_if_t = typename std::enable_if<C, T ...>::type;
template<class A, class B>
constexpr bool identical(const A& a, const B& b)
{
static_assert(std::is_same<A, B>::value, "different types");
return a==b;
}
}
}
namespace sg14 {
using _digits_type = int;
template<class T, class Enable = void>
struct is_composite : std::false_type {
static_assert(!std::is_reference<T>::value, "T is a reference");
static_assert(!std::is_const<T>::value, "T is const");
static_assert(!std::is_volatile<T>::value, "T is volatile");
};
namespace _num_traits_impl {
template<class ... Args>
struct are_composite;
template<>
struct are_composite<> : std::false_type {
};
template<class ArgHead, class ... ArgTail>
struct are_composite<ArgHead, ArgTail...>
: std::integral_constant<bool, is_composite<typename std::decay<ArgHead>::type>::value || are_composite<ArgTail...>::value> {
};
template<_digits_type MinNumDigits, class Smaller, class T>
struct enable_for_range
: std::enable_if<MinNumDigits <= std::numeric_limits<T>::digits &&
std::numeric_limits<Smaller>::digits < MinNumDigits> {
};
template<_digits_type MinNumDigits, class Smallest>
struct enable_for_range<MinNumDigits, void, Smallest>
: std::enable_if<MinNumDigits <= std::numeric_limits<Smallest>::digits> {
};
template<_digits_type MinNumDigits, class Smaller, class T>
using enable_for_range_t = typename enable_for_range<MinNumDigits, Smaller, T>::type;
template<_digits_type MinNumDigits, class Enable = void>
struct set_digits_signed;
template<_digits_type MinNumDigits>
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, void, std::int8_t>> {
using type = std::int8_t;
};
template<_digits_type MinNumDigits>
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int8_t, std::int16_t>> {
using type = std::int16_t;
};
template<_digits_type MinNumDigits>
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int16_t, std::int32_t>> {
using type = std::int32_t;
};
template<_digits_type MinNumDigits>
struct set_digits_signed<MinNumDigits, enable_for_range_t<MinNumDigits, std::int32_t, std::int64_t>> {
using type = std::int64_t;
};
template<_digits_type MinNumDigits, class Enable = void>
struct set_digits_unsigned;
template<_digits_type MinNumDigits>
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, void, std::uint8_t>> {
using type = std::uint8_t;
};
template<_digits_type MinNumDigits>
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint8_t, std::uint16_t>> {
using type = std::uint16_t;
};
template<_digits_type MinNumDigits>
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint16_t, std::uint32_t>> {
using type = std::uint32_t;
};
template<_digits_type MinNumDigits>
struct set_digits_unsigned<MinNumDigits, enable_for_range_t<MinNumDigits, std::uint32_t, std::uint64_t>> {
using type = std::uint64_t;
};
template<class Integer, _digits_type MinNumDigits>
using set_digits_integer = typename std::conditional<
std::numeric_limits<Integer>::is_signed,
set_digits_signed<MinNumDigits>,
set_digits_unsigned<MinNumDigits>>::type;
}
template<class T, class Enable = void>
struct digits : std::integral_constant<_digits_type, std::numeric_limits<T>::digits> {
};
template<class T, _digits_type Digits, class Enable = void>
struct set_digits;
template<class T, _digits_type Digits>
struct set_digits<T, Digits, _impl::enable_if_t<std::is_integral<T>::value>>
: _num_traits_impl::set_digits_integer<T, Digits> {
};
template<class T, _digits_type Digits>
using set_digits_t = typename set_digits<T, Digits>::type;
template<class T>
struct is_signed : std::integral_constant<bool, std::numeric_limits<T>::is_signed> {
};
template<class, class = void>
struct make_signed;
template<class T>
struct make_signed<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_signed<T> {
};
template<class T>
using make_signed_t = typename make_signed<T>::type;
template<class, class = void>
struct make_unsigned;
template<class T>
struct make_unsigned<T, _impl::enable_if_t<std::is_integral<T>::value>> : std::make_unsigned<T> {
};
template<class T>
using make_unsigned_t = typename make_unsigned<T>::type;
namespace _impl {
template<class T, bool IsSigned = true>
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;
template<class T1, class T2>
struct common_signedness {
static constexpr bool _are_signed = std::numeric_limits<T1>::is_signed | std::numeric_limits<T2>::is_signed;
using type = typename std::common_type<make_signed_t<T1, _are_signed>,
make_signed_t<T2, _are_signed>>::type;
};
template<class T1, class T2>
using common_signedness_t = typename common_signedness<T1, T2>::type;
template<class T, class Enable = void>
struct unsigned_or_float;
template<class T>
struct unsigned_or_float<T, enable_if_t<std::numeric_limits<T>::is_iec559>> {
using type = T;
};
template<class T>
struct unsigned_or_float<T, enable_if_t<!std::numeric_limits<T>::is_iec559>> : make_unsigned<T> {
};
template<class T>
using unsigned_or_float_t = typename unsigned_or_float<T>::type;
template<class Encompasser, class Encompassed, class Enable = void>
struct encompasses_lower;
template<class Encompasser, class Encompassed>
struct encompasses_lower<Encompasser, Encompassed,
enable_if_t<std::numeric_limits<Encompasser>::is_signed
&& std::numeric_limits<Encompassed>::is_signed>> {
static constexpr bool value = std::numeric_limits<Encompasser>::lowest()
<= std::numeric_limits<Encompassed>::lowest();
};
template<class Encompasser, class Encompassed>
struct encompasses_lower<Encompasser, Encompassed,
enable_if_t<!std::numeric_limits<Encompassed>::is_signed>> : std::true_type {
};
template<class Encompasser, class Encompassed>
struct encompasses_lower<Encompasser, Encompassed,
enable_if_t<!std::numeric_limits<Encompasser>::is_signed
&& std::numeric_limits<Encompassed>::is_signed>> : std::false_type {
};
template<class Encompasser, class Encompassed>
struct encompasses {
static constexpr bool _lower = encompasses_lower<Encompasser, Encompassed>::value;
static constexpr bool _upper =
static_cast<unsigned_or_float_t<Encompasser>>(std::numeric_limits<Encompasser>::max())
>= static_cast<unsigned_or_float_t<Encompassed>>(std::numeric_limits<Encompassed>::max());
static constexpr bool value = _lower && _upper;
};
template<class T>
struct is_integer_or_float : std::integral_constant<
bool,
std::numeric_limits<T>::is_integer || std::numeric_limits<T>::is_iec559> {
};
}
template<class Number, class Enable = void>
struct to_rep {
constexpr Number operator()(const Number &number) const {
return number;
}
};
namespace _impl {
template<class Number, class Enable = void>
constexpr auto to_rep(const Number &number)
-> decltype(sg14::to_rep<Number>()(number)) {
return sg14::to_rep<Number>()(number);
}
}
template<class Number, class Enable = void>
struct from_rep {
template<class Rep>
constexpr Number operator()(const Rep &rep) const {
return static_cast<Number>(rep);
}
};
namespace _impl {
template<class Number, class Rep>
constexpr auto from_rep(const Rep &rep)
-> decltype(sg14::from_rep<Number>()(rep)) {
return sg14::from_rep<Number>()(rep);
}
}
template<class Result, class F, class ... Args,
_impl::enable_if_t<!_num_traits_impl::are_composite<Args ...>::value, int> dummy = 0>
constexpr Result for_rep(F f, Args &&...args) {
return f(std::forward<Args>(args)...);
}
template<class Result, class F, class ... Args,
_impl::enable_if_t<_num_traits_impl::are_composite<Args ...>::value, int> dummy = 0>
constexpr Result for_rep(F f, Args &&...args) {
return for_rep<Result>(f, _impl::to_rep<typename std::decay<Args>::type>(std::forward<Args>(args))...);
}
template<class Number, class Value, class Enable = void>
struct from_value;
template<class Number, class Value>
struct from_value<Number, Value, _impl::enable_if_t<std::is_integral<Number>::value>> {
using type = Number;
};
template<class Number, class Value>
using from_value_t = typename from_value<Number, Value>::type;
namespace _impl {
template<class Number, class Value>
constexpr auto from_value(const Value &value)
-> sg14::from_value_t<Number, Value> {
return value;
}
}
namespace _num_traits_impl {
template<class T>
using scale_result_type = decltype(std::declval<T>() * std::declval<T>());
template<class T>
constexpr scale_result_type<T> pown(int base, int exp) {
return exp
? pown<T>(base, exp - 1) * static_cast<scale_result_type<T>>(base)
: static_cast<scale_result_type<T>>(1);
}
template<class T>
constexpr scale_result_type<T> pow2(int exp) {
return scale_result_type<T>{1} << exp;
}
template<class T>
constexpr scale_result_type<T> pow(int base, int exp) {
return (base == 2) ? pow2<T>(exp) : pown<T>(base, exp);
}
}
template<class T>
struct scale {
constexpr auto operator()(const T &i, int base, int exp) const
-> _num_traits_impl::scale_result_type<T> {
return _impl::from_rep<_num_traits_impl::scale_result_type<T>>(
(exp < 0)
? _impl::to_rep<T>(i) / _num_traits_impl::pow<T>(base, -exp)
: _impl::to_rep<T>(i) * _num_traits_impl::pow<T>(base, exp));
}
};
namespace _impl {
template<class T>
constexpr auto scale(const T &i, int base, int exp)
-> decltype(sg14::scale<T>()(i, base, exp)) {
return sg14::scale<T>()(i, base, exp);
}
}
}
namespace sg14 {
namespace _numeric_impl {
template<class Integer>
constexpr int trailing_bits_positive(Integer value, int mask_bits = sizeof(Integer)*8/2)
{
return ((value & ((Integer{1} << mask_bits)-1))==0)
? mask_bits+trailing_bits_positive(value/(Integer{1} << mask_bits), mask_bits)
: (mask_bits>1)
? trailing_bits_positive(value, mask_bits/2)
: 0;
}
template<class Integer, class Enable = void>
struct trailing_bits {
static constexpr int f(Integer value)
{
return value ? trailing_bits_positive(value) : 0;
}
};
template<class Integer>
struct trailing_bits<Integer, _impl::enable_if_t<std::numeric_limits<Integer>::is_signed>> {
static constexpr int f(Integer value)
{
return (value>0)
? trailing_bits_positive(value)
: (value<0)
? trailing_bits_positive(-value)
: 0;
}
};
}
template<class Integer>
constexpr int trailing_bits(Integer value)
{
return _numeric_impl::trailing_bits<Integer>::f(value);
}
namespace _numeric_impl {
template<class Integer>
constexpr int used_bits_positive(Integer value, int mask_bits = sizeof(Integer)*8/2)
{
return (value>=(Integer{1} << mask_bits))
? mask_bits+used_bits_positive(value/(Integer{1} << mask_bits), mask_bits)
: (mask_bits>1)
? used_bits_positive(value, mask_bits/2)
: 1;
}
}
namespace _impl {
template<class Integer>
constexpr int used_bits_symmetric(Integer value)
{
return (value>0)
? _numeric_impl::used_bits_positive<Integer>(value)
: (value<0)
? _numeric_impl::used_bits_positive<Integer>(-value)
: 0;
}
}
namespace _numeric_impl {
struct used_bits {
template<class Integer>
constexpr _impl::enable_if_t<!std::numeric_limits<Integer>::is_signed, int> operator()(Integer value) const
{
return value ? used_bits_positive(value) : 0;
}
template<class Integer, class = _impl::enable_if_t<std::numeric_limits<Integer>::is_signed, int>>
constexpr int operator()(Integer value) const
{
return (value>0)
? used_bits_positive(value)
: (value==0)
? 0
: used_bits()(Integer(-1)-value);
}
};
}
template<class Integer>
constexpr int used_bits(Integer value)
{
return for_rep<int>(_numeric_impl::used_bits(), value);
}
template<class Integer>
constexpr int leading_bits(const Integer& value)
{
return digits<Integer>::value-used_bits(value);
}
}
namespace sg14 {
namespace _impl {
template<class T>
constexpr T max(T a, T b)
{
return (a<b) ? b : a;
}
template<class T>
constexpr T min(T a, T b)
{
return (a<b) ? a : b;
}
struct arithmetic_op {
static constexpr bool is_arithmetic = true;
};
struct comparison_op {
static constexpr bool is_comparison = true;
};
struct minus_op : arithmetic_op {
template<class Rhs>
constexpr auto operator()(const Rhs& rhs) const -> decltype(-rhs)
{
return -rhs;
}
};
struct plus_op : arithmetic_op {
template<class Rhs>
constexpr auto operator()(const Rhs& rhs) const -> decltype(+rhs)
{
return +rhs;
}
};
struct add_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs+rhs)
{
return lhs+rhs;
}
};
struct subtract_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs-rhs)
{
return lhs-rhs;
}
};
struct multiply_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs*rhs)
{
return lhs*rhs;
}
};
struct divide_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs/rhs)
{
return lhs/rhs;
}
};
struct bitwise_or_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs|rhs)
{
return lhs|rhs;
}
};
struct bitwise_and_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs&rhs)
{
return lhs&rhs;
}
};
struct bitwise_xor_op : arithmetic_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs^rhs)
{
return lhs^rhs;
}
};
struct equal_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs==rhs)
{
return lhs==rhs;
}
};
struct not_equal_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs!=rhs)
{
return lhs!=rhs;
}
};
struct less_than_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs<rhs)
{
return lhs<rhs;
}
};
struct greater_than_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs>rhs)
{
return lhs>rhs;
}
};
struct less_than_or_equal_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs<=rhs)
{
return lhs<=rhs;
}
};
struct greater_than_or_equal_op : comparison_op {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const -> decltype(lhs>=rhs)
{
return lhs>=rhs;
}
};
static constexpr plus_op plus_tag {};
static constexpr minus_op minus_tag {};
static constexpr add_op add_tag {};
static constexpr subtract_op subtract_tag {};
static constexpr multiply_op multiply_tag {};
static constexpr divide_op divide_tag {};
static constexpr bitwise_or_op bitwise_or_tag {};
static constexpr bitwise_and_op bitwise_and_tag {};
static constexpr bitwise_xor_op bitwise_xor_tag {};
static constexpr equal_op equal_tag {};
static constexpr not_equal_op not_equal_tag {};
static constexpr less_than_op less_than_tag {};
static constexpr greater_than_op greater_than_tag {};
static constexpr less_than_or_equal_op less_than_or_equal_tag {};
static constexpr greater_than_or_equal_op greater_than_or_equal_tag {};
template<class Operator, class Lhs, class Rhs>
using op_result = decltype(Operator()(std::declval<Lhs>(), std::declval<Rhs>()));
}
}
namespace sg14 {
namespace _const_integer_impl {
constexpr std::intmax_t combine(int, std::intmax_t p)
{
return p;
}
template<class... TT>
constexpr std::intmax_t combine(int base, std::intmax_t val, int p0, TT... pp)
{
return combine(base, val * base + p0, pp...);
}
constexpr int parse_dec(char C)
{
return (C>='0' && C<='9')
? C-'0'
: throw std::out_of_range("only decimal digits are allowed");
}
constexpr int parse_hex(char C) {
return (C >= '0' && C <= '9')
? C - '0'
: (C >= 'a' && C <= 'f')
? C - 'a'
: (C >= 'A' && C <= 'F')
? C - 'A'
: throw std::out_of_range("only decimal digits are allowed")
;
}
template<char... Digits>
struct digits_to_integral {
static constexpr std::intmax_t value = combine(10, 0, parse_dec(Digits)...);
};
template<char... Digits>
struct digits_to_integral<'0', 'x', Digits...> {
static constexpr std::intmax_t value = combine(16, 0, parse_hex(Digits)...);
};
template<char... Digits>
struct digits_to_integral<'0', 'X', Digits...> {
static constexpr std::intmax_t value = combine(16, 0, parse_hex(Digits)...);
};
}
template<
class Integral,
Integral Value,
int Digits = used_bits(Value),
int Exponent = trailing_bits(Value)>
class const_integer {
public:
using value_type = Integral;
static constexpr value_type value = Value;
template<class T>
constexpr explicit operator T() const { return value; }
static constexpr int digits = Digits;
static_assert(
digits == used_bits(Value),
"defaulted non-type template parameters should not be specified explicitly");
static constexpr int exponent = Exponent;
static_assert(
exponent == trailing_bits(Value),
"defaulted non-type template parameters should not be specified explicitly");
};
namespace _const_integer_impl {
template<class Lhs, class Rhs, class Type>
struct enable_if_op;
template<
class LhsIntegral, LhsIntegral LhsValue, int LhsDigits, int LhsExponent,
class RhsIntegral, RhsIntegral RhsValue, int RhsDigits, int RhsExponent,
class Type>
struct enable_if_op<
const_integer<LhsIntegral, LhsValue, LhsDigits, LhsExponent>,
const_integer<RhsIntegral, RhsValue, RhsDigits, RhsExponent>,
Type> {
using type = Type;
};
template<
class LhsIntegral, LhsIntegral LhsValue, int LhsDigits, int LhsExponent,
class Rhs,
class Type>
struct enable_if_op<
const_integer<LhsIntegral, LhsValue, LhsDigits, LhsExponent>,
Rhs,
Type> {
using type = Type;
};
template<
class Lhs,
class RhsIntegral, RhsIntegral RhsValue, int RhsDigits, int RhsExponent,
class Type>
struct enable_if_op<
Lhs,
const_integer<RhsIntegral, RhsValue, RhsDigits, RhsExponent>,
Type> {
using type = Type;
};
template<
class Operator,
class Lhs,
class RhsIntegral, RhsIntegral RhsValue, int RhsDigits, int RhsExponent,
class = _impl::enable_if_t<std::is_integral<Lhs>::value>>
constexpr auto operate(
const Lhs& lhs,
const const_integer<RhsIntegral, RhsValue, RhsDigits, RhsExponent>&,
Operator op)
-> decltype(op(lhs, RhsValue)) {
return op(lhs, RhsValue);
}
template<
class Operator,
class LhsIntegral, LhsIntegral LhsValue, int LhsDigits, int LhsExponent,
class Rhs,
class = _impl::enable_if_t<std::is_integral<Rhs>::value, int>>
constexpr auto operate(
const const_integer<LhsIntegral, LhsValue, LhsDigits, LhsExponent>&,
const Rhs& rhs,
Operator op)
-> decltype(op(LhsValue, rhs)) {
return op(LhsValue, rhs);
}
template<
class Operator,
class LhsIntegral, LhsIntegral LhsValue, int LhsDigits, int LhsExponent,
class RhsIntegral, RhsIntegral RhsValue, int RhsDigits, int RhsExponent>
constexpr auto operate(
const const_integer<LhsIntegral, LhsValue, LhsDigits, LhsExponent>&,
const const_integer<RhsIntegral, RhsValue, RhsDigits, RhsExponent>&,
Operator)
-> decltype(const_integer<_impl::op_result<Operator, LhsIntegral, RhsIntegral>, Operator()(LhsValue, RhsValue)>{}) {
return const_integer<_impl::op_result<Operator, LhsIntegral, RhsIntegral>, Operator()(LhsValue, RhsValue)>{};
}
}
template<class Lhs, class Rhs, typename _const_integer_impl::enable_if_op<Lhs, Rhs, int>::type dummy = 0>
constexpr auto operator+(const Lhs& lhs, const Rhs& rhs)
-> decltype(_const_integer_impl::operate(lhs, rhs, _impl::add_tag))
{
return _const_integer_impl::operate(lhs, rhs, _impl::add_tag);
}
template<class Lhs, class Rhs, typename _const_integer_impl::enable_if_op<Lhs, Rhs, int>::type dummy = 0>
constexpr auto operator-(const Lhs& lhs, const Rhs& rhs)
-> decltype(_const_integer_impl::operate(lhs, rhs, _impl::subtract_tag))
{
return _const_integer_impl::operate(lhs, rhs, _impl::subtract_tag);
}
template<class Lhs, class Rhs, typename _const_integer_impl::enable_if_op<Lhs, Rhs, int>::type dummy = 0>
constexpr auto operator*(const Lhs& lhs, const Rhs& rhs)
-> decltype(_const_integer_impl::operate(lhs, rhs, _impl::multiply_tag))
{
return _const_integer_impl::operate(lhs, rhs, _impl::multiply_tag);
}
template<class Lhs, class Rhs, typename _const_integer_impl::enable_if_op<Lhs, Rhs, int>::type dummy = 0>
constexpr auto operator/(const Lhs& lhs, const Rhs& rhs)
-> decltype(_const_integer_impl::operate(lhs, rhs, _impl::divide_tag))
{
return _const_integer_impl::operate(lhs, rhs, _impl::divide_tag);
}
namespace _const_integer_impl {
template<
class Operator,
class LhsIntegral, LhsIntegral LhsValue, int LhsDigits, int LhsExponent,
class RhsIntegral, RhsIntegral RhsValue, int RhsDigits, int RhsExponent>
constexpr auto compare(
const const_integer<LhsIntegral, LhsValue, LhsDigits, LhsExponent>&,
const const_integer<RhsIntegral, RhsValue, RhsDigits, RhsExponent>&,
Operator op)
-> decltype(op(LhsValue, RhsValue))
{
return op(LhsValue, RhsValue);
}
}
template<class Lhs, class Rhs, typename _const_integer_impl::enable_if_op<Lhs, Rhs, int>::type dummy = 0>
constexpr auto operator==(const Lhs& lhs, const Rhs& rhs)
-> decltype(_const_integer_impl::compare(lhs, rhs, _impl::equal_tag))
{
return _const_integer_impl::compare(lhs, rhs, _impl::equal_tag);
}
template<class RhsIntegral, RhsIntegral RhsValue>
constexpr const_integer<decltype(-RhsValue), -RhsValue>
operator-(const_integer<RhsIntegral, RhsValue>) noexcept
{
return const_integer<decltype(-RhsValue), -RhsValue>{};
}
template<class T>
struct is_const_integer : std::false_type {};
template<class Integral, Integral Value>
struct is_const_integer<const_integer<Integral, Value>> : std::true_type {};
namespace literals {
template<char... Digits>
constexpr auto operator "" _c()
-> const_integer<std::intmax_t, _const_integer_impl::digits_to_integral<Digits...>::value>
{
return {};
}
}
}
namespace std {
template<class Integral, Integral Value, int Digits, int Zeros, class Rhs>
struct common_type<sg14::const_integer<Integral, Value, Digits, Zeros>, Rhs>
: common_type<Integral, Rhs> {
};
template<class Lhs, class Integral, Integral Value, int Digits, int Zeros>
struct common_type<Lhs, sg14::const_integer<Integral, Value, Digits, Zeros>>
: common_type<Lhs, Integral> {
};
template<class Integral, Integral Value, int Digits, int Zeros>
struct numeric_limits<sg14::const_integer<Integral, Value, Digits, Zeros>>
: numeric_limits<Integral> {
static constexpr int digits = Digits;
};
}
namespace sg14 {
namespace _impl {
template<class Derived, class Rep>
class number_base {
public:
using rep = Rep;
using _derived = Derived;
number_base() = default;
constexpr number_base(const rep& r)
: _rep(r) { }
template<class T>
number_base& operator=(const T& r) {
_rep = r;
return static_cast<Derived&>(*this);
}
explicit constexpr operator bool() const
{
return static_cast<bool>(_rep);
}
constexpr const rep& data() const
{
return _rep;
}
static constexpr Derived from_data(const rep& r)
{
return Derived(r);
}
private:
rep _rep;
};
template<class Derived, class Enable = void>
struct is_class_derived_from_number_base : std::false_type {};
template<class Derived>
struct is_class_derived_from_number_base<
Derived,
enable_if_t<std::is_base_of<number_base<Derived, typename Derived::rep>, Derived>::value>>
: std::true_type {};
template<class T, class Enable = void>
struct is_derived_from_number_base : std::false_type {};
template<class Derived>
struct is_derived_from_number_base<Derived, enable_if_t<std::is_class<Derived>::value>>
: is_class_derived_from_number_base<Derived> { };
template<class Former, class Latter>
struct precedes {
static constexpr bool value =
(std::is_floating_point<Former>::value && !std::is_floating_point<Latter>::value)
|| (is_derived_from_number_base<Former>::value &&
!(is_derived_from_number_base<Latter>::value
|| std::is_floating_point<Latter>::value));
};
template<
class Operator, class Lhs, class RhsDerived, class RhsRep,
enable_if_t <precedes<Lhs, RhsDerived>::value, std::nullptr_t> = nullptr>
constexpr auto operate(const Lhs& lhs, const number_base<RhsDerived, RhsRep>& rhs, Operator op)
-> decltype(op(lhs, to_rep(static_cast<const RhsDerived&>(rhs))))
{
return op(lhs, to_rep(static_cast<const RhsDerived&>(rhs)));
}
template<
class Operator, class LhsDerived, class LhsRep, class Rhs,
enable_if_t <precedes<Rhs, LhsDerived>::value, std::nullptr_t> = nullptr>
constexpr auto operate(const number_base<LhsDerived, LhsRep>& lhs, const Rhs& rhs, Operator op)
-> decltype(op(to_rep(static_cast<const LhsDerived&>(lhs)), rhs))
{
return op(to_rep(static_cast<const LhsDerived&>(lhs)), rhs);
}
template<
class Operator, class Lhs, class RhsDerived, class RhsRep,
enable_if_t <precedes<RhsDerived, Lhs>::value, std::nullptr_t> = nullptr>
constexpr auto operate(const Lhs& lhs, const number_base<RhsDerived, RhsRep>& rhs, Operator op)
-> decltype(op(_impl::from_value<RhsDerived>(lhs), static_cast<const RhsDerived&>(rhs))) {
return op(from_value<RhsDerived>(lhs), static_cast<const RhsDerived&>(rhs));
}
template<
class Operator, class LhsDerived, class LhsRep, class Rhs,
enable_if_t <precedes<LhsDerived, Rhs>::value, std::nullptr_t> = nullptr>
constexpr auto operate(const number_base<LhsDerived, LhsRep>& lhs, const Rhs& rhs, Operator op)
-> decltype(op(static_cast<const LhsDerived &>(lhs), from_value<LhsDerived>(rhs)))
{
return op(static_cast<const LhsDerived &>(lhs), from_value<LhsDerived>(rhs));
}
template<class Operator, class RhsDerived, class RhsRep>
constexpr auto operate(const number_base<RhsDerived, RhsRep>& rhs, Operator op)
-> decltype(op(rhs.data()))
{
return op(rhs.data());
}
template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
auto operator+=(Lhs& lhs, const Rhs& rhs)
-> decltype(lhs = lhs + rhs)
{
return lhs = lhs + rhs;
}
template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
auto operator-=(Lhs& lhs, const Rhs& rhs)
-> decltype(lhs = lhs - rhs)
{
return lhs = lhs - rhs;
}
template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
auto operator*=(Lhs& lhs, const Rhs& rhs)
-> decltype(lhs = lhs * rhs)
{
return lhs = lhs * rhs;
}
template<class Lhs, class Rhs, class = enable_if_t <is_derived_from_number_base<Lhs>::value>>
auto operator/=(Lhs& lhs, const Rhs& rhs)
-> decltype(lhs = lhs / rhs)
{
return lhs = lhs / rhs;
}
template<class RhsDerived, class RhsRep>
constexpr auto operator+(const number_base<RhsDerived, RhsRep>& rhs)
-> decltype(operate(rhs, plus_tag))
{
return operate(rhs, plus_tag);
}
template<class RhsDerived, class RhsRep>
constexpr auto operator-(const number_base<RhsDerived, RhsRep>& rhs)
-> decltype(operate(rhs, minus_tag))
{
return operate(rhs, minus_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator+(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, add_tag))
{
return operate(lhs, rhs, add_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator-(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, subtract_tag))
{
return operate(lhs, rhs, subtract_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator*(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, multiply_tag))
{
return operate(lhs, rhs, multiply_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator/(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, divide_tag))
{
return operate(lhs, rhs, divide_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator|(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, bitwise_or_tag))
{
return operate(lhs, rhs, bitwise_or_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator&(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, bitwise_and_tag))
{
return operate(lhs, rhs, bitwise_and_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator^(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, bitwise_xor_tag))
{
return operate(lhs, rhs, bitwise_xor_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator==(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, equal_tag))
{
return operate(lhs, rhs, equal_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator!=(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, not_equal_tag))
{
return operate(lhs, rhs, not_equal_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator<(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, less_than_tag))
{
return operate(lhs, rhs, less_than_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator>(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, greater_than_tag))
{
return operate(lhs, rhs, greater_than_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator<=(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, less_than_or_equal_tag))
{
return operate(lhs, rhs, less_than_or_equal_tag);
}
template<class Lhs, class Rhs>
constexpr auto operator>=(const Lhs& lhs, const Rhs& rhs)
-> decltype(operate(lhs, rhs, greater_than_or_equal_tag))
{
return operate(lhs, rhs, greater_than_or_equal_tag);
}
}
template<class Number>
struct is_composite<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> : std::true_type {
};
namespace _impl {
template<class Number>
struct get_rep;
template<class Number>
using get_rep_t = typename get_rep<Number>::type;
template<class Number, class NewRep, class Enable = void>
struct set_rep;
template<class Number, class NewRep>
using set_rep_t = typename set_rep<Number, NewRep>::type;
}
template<class Number>
struct make_signed<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
using type = _impl::set_rep_t<Number, make_signed_t<_impl::get_rep_t<Number>>>;
};
template<class Number>
struct make_unsigned<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
using type = _impl::set_rep_t<Number, make_unsigned_t<_impl::get_rep_t<Number>>>;
};
template<class Number>
struct from_rep<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
template<class Rep>
constexpr auto operator()(const Rep &rep) const -> Number {
return Number::from_data(static_cast<typename Number::rep>(rep));
}
};
template<class Number>
struct to_rep<Number, _impl::enable_if_t<_impl::is_derived_from_number_base<Number>::value>> {
constexpr auto operator()(const typename Number::_derived& number) const
-> decltype(number.data()){
return number.data();
}
};
template<class Derived, class Rep>
struct scale<_impl::number_base<Derived, Rep>> {
template<class Input>
constexpr Rep operator()(const Input &i, int base, int exp) const {
return (exp < 0)
? _impl::to_rep(i) / _num_traits_impl::pow<Rep>(base, -exp)
: _impl::to_rep(i) * _num_traits_impl::pow<Rep>(base, exp);
}
};
}
namespace std {
template<class Derived, class Rep>
struct numeric_limits<sg14::_impl::number_base<Derived, Rep>>
: numeric_limits<Rep> {
using _value_type = Derived;
using _rep = typename _value_type::rep;
using _rep_numeric_limits = numeric_limits<_rep>;
static constexpr _value_type min() noexcept
{
return _value_type::from_data(_rep_numeric_limits::min());
}
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 _value_type epsilon() noexcept
{
return _value_type::from_data(_rep_numeric_limits::round_error());
}
static constexpr _value_type round_error() noexcept
{
return static_cast<_value_type>(_rep_numeric_limits::round_error());
}
static constexpr _value_type infinity() noexcept
{
return static_cast<_value_type>(_rep_numeric_limits::infinity());
}
static constexpr _value_type quiet_NaN() noexcept
{
return static_cast<_value_type>(_rep_numeric_limits::quiet_NaN());
}
static constexpr _value_type signaling_NaN() noexcept
{
return static_cast<_value_type>(_rep_numeric_limits::signaling_NaN());
}
static constexpr _value_type denorm_min() noexcept
{
return static_cast<_value_type>(_rep_numeric_limits::denorm_min());
}
};
}
namespace sg14 {
template<int Digits, class Narrowest>
class elastic_integer;
namespace _elastic_integer_impl {
template<int Digits, class Narrowest>
struct base_class {
static constexpr _digits_type digits = Digits;
static constexpr _digits_type rep_digits = _impl::max(sg14::digits<Narrowest>::value, digits);
using rep = typename set_digits<Narrowest, rep_digits>::type;
using type = _impl::number_base<elastic_integer<Digits, Narrowest>, rep>;
};
template<int Digits, class Narrowest>
using base_class_t = typename base_class<Digits, Narrowest>::type;
}
template<int Digits, class Narrowest>
struct digits<elastic_integer<Digits, Narrowest>> : std::integral_constant<_digits_type, Digits> {
static constexpr _digits_type value = Digits;
};
template<int Digits, class Narrowest, _digits_type MinNumBits>
struct set_digits<elastic_integer<Digits, Narrowest>, MinNumBits> {
using type = elastic_integer<MinNumBits, Narrowest>;
};
namespace _impl {
template<int Digits, class Narrowest>
struct get_rep<elastic_integer<Digits, Narrowest>> {
using type = Narrowest;
};
template<int Digits, class OldNarrowest, class NewNarrowest>
struct set_rep<elastic_integer<Digits, OldNarrowest>, NewNarrowest> {
using type = elastic_integer<Digits, NewNarrowest>;
};
}
template<int Digits, class Narrowest, class Value>
struct from_value<elastic_integer<Digits, Narrowest>, Value> {
using type = elastic_integer<sg14::digits<Value>::value, sg14::_impl::make_signed_t<Narrowest, sg14::is_signed<Value>::value>>;
};
template<int Digits, class Narrowest>
struct scale<elastic_integer<Digits, Narrowest>> {
using _value_type = elastic_integer<Digits, Narrowest>;
constexpr _value_type operator()(const _value_type& i, int base, int exp) const {
using _rep = typename _value_type::rep;
return _value_type{ _impl::scale(i.data(), base, exp) };
}
};
template<int Digits, class Narrowest = int>
class elastic_integer : public _elastic_integer_impl::base_class_t<Digits, Narrowest> {
static_assert(Digits > 0, "type requires positive number of digits");
using _base = _elastic_integer_impl::base_class_t<Digits, Narrowest>;
public:
static constexpr int digits = Digits;
using narrowest = Narrowest;
using rep = typename _base::rep;
constexpr elastic_integer() = default;
constexpr elastic_integer(const elastic_integer& rhs)
:_base(rhs)
{
}
template<class Number, _impl::enable_if_t<std::numeric_limits<Number>::is_specialized, int> Dummy = 0>
constexpr elastic_integer(Number n)
: _base(static_cast<rep>(n))
{
}
template<int FromWidth, class FromNarrowest>
explicit constexpr elastic_integer(const elastic_integer<FromWidth, FromNarrowest>& rhs)
:_base(static_cast<rep>(rhs.data()))
{
}
template<class Integral, Integral Value, int Exponent>
constexpr elastic_integer(const_integer<Integral, Value, Digits, Exponent>)
: _base(static_cast<rep>(Value))
{
static_assert(!sg14::is_signed<Integral>::value || sg14::is_signed<rep>::value, "initialization by out-of-range value");
}
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
elastic_integer& operator=(S s)
{
_base::operator=(floating_point_to_rep(s));
return *this;
}
template<class S>
explicit constexpr operator S() const
{
return static_cast<S>(_base::data());
}
};
template<
class Integral, Integral Value>
constexpr auto make_elastic_integer(const_integer<Integral, Value>)
-> elastic_integer<used_bits(Value)>
{
return elastic_integer<used_bits(Value)>{Value};
}
template<class Narrowest = int, class Integral, _impl::enable_if_t<!is_const_integer<Integral>::value, int> Dummy = 0>
constexpr auto make_elastic_integer(const Integral& value)
-> decltype(elastic_integer<std::numeric_limits<Integral>::digits, Narrowest>{value})
{
return elastic_integer<std::numeric_limits<Integral>::digits, Narrowest>{value};
}
namespace _elastic_integer_impl {
template<class ElasticInteger>
struct is_elastic_integer : std::false_type {
};
template<int Digits, class Narrowest>
struct is_elastic_integer<elastic_integer<Digits, Narrowest>> : std::true_type {
};
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);
};
}
template<int RhsDigits, class RhsNarrowest>
constexpr auto operator~(const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
-> elastic_integer<RhsDigits, RhsNarrowest>
{
using elastic_integer = elastic_integer<RhsDigits, RhsNarrowest>;
using rep = typename elastic_integer::rep;
return elastic_integer::from_data(
static_cast<rep>(
rhs.data()
^ ((static_cast<rep>(~0)) >> (std::numeric_limits<rep>::digits - RhsDigits))));
}
template<int LhsDigits, class LhsNarrowest, class Rhs>
constexpr auto operator<<(const elastic_integer<LhsDigits, LhsNarrowest>& lhs, const Rhs& rhs)
-> elastic_integer<LhsDigits, LhsNarrowest>
{
return elastic_integer<LhsDigits, LhsNarrowest>::from_data(lhs.data() << rhs);
}
template<class Lhs, int RhsDigits, class RhsNarrowest>
constexpr auto operator<<(const Lhs& lhs, const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
-> decltype(lhs << 0)
{
return lhs << rhs.data();
}
template<int LhsDigits, class LhsNarrowest, class Rhs>
constexpr auto operator>>(const elastic_integer<LhsDigits, LhsNarrowest>& lhs, const Rhs& rhs)
-> elastic_integer<LhsDigits, LhsNarrowest>
{
return elastic_integer<LhsDigits, LhsNarrowest>::from_data(lhs.data() >> rhs);
}
template<class Lhs, int RhsDigits, class RhsNarrowest>
constexpr auto operator>>(const Lhs& lhs, const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
-> decltype(lhs >> 0)
{
return lhs >> rhs.data();
}
namespace _impl {
template<int FromDigits, class FromNarrowest, int OtherDigits, class OtherNarrowest,
_impl::enable_if_t<FromDigits!=OtherDigits || !std::is_same<FromNarrowest, OtherNarrowest>::value, std::nullptr_t> Dummy = nullptr>
constexpr auto cast_to_common_type(
const elastic_integer<FromDigits, FromNarrowest>& from,
const elastic_integer<OtherDigits, OtherNarrowest>&)
-> decltype(static_cast<_impl::common_type_t<
elastic_integer<FromDigits, FromNarrowest>,
elastic_integer<OtherDigits, OtherNarrowest>>>(from)) {
return static_cast<_impl::common_type_t<
elastic_integer<FromDigits, FromNarrowest>,
elastic_integer<OtherDigits, OtherNarrowest>>>(from);
};
template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
bool Enable = Operator::is_comparison>
constexpr auto operate(
const elastic_integer<LhsDigits, LhsNarrowest>& lhs,
const elastic_integer<RhsDigits, RhsNarrowest>& rhs,
Operator op)
-> decltype(op(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs)))
{
return op(cast_to_common_type(lhs, rhs), cast_to_common_type(rhs, lhs));
}
template<class Operator, int Digits, class Narrowest,
class = enable_if_t<Operator::is_comparison>>
constexpr auto
operate(const elastic_integer<Digits, Narrowest>& lhs, const elastic_integer<Digits, Narrowest>& rhs, Operator op)
-> decltype(op(lhs.data(), rhs.data()))
{
return op(lhs.data(), rhs.data());
}
}
namespace _impl {
template<class Operation, class LhsTraits, class RhsTraits>
struct policy;
template<class LhsTraits, class RhsTraits>
struct policy<_impl::add_op, LhsTraits, RhsTraits> {
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits)+1;
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
};
template<class LhsTraits, class RhsTraits>
struct policy<_impl::subtract_op, LhsTraits, RhsTraits> {
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits) + (LhsTraits::is_signed | RhsTraits::is_signed);
static constexpr bool is_signed = true;
};
template<class LhsTraits, class RhsTraits>
struct policy<_impl::multiply_op, 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_op, LhsTraits, RhsTraits> {
static constexpr int digits = LhsTraits::digits;
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
};
template<class LhsTraits, class RhsTraits>
struct policy<_impl::bitwise_or_op, LhsTraits, RhsTraits> {
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits);
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
};
template<class LhsTraits, class RhsTraits>
struct policy<_impl::bitwise_and_op, LhsTraits, RhsTraits> {
static constexpr int digits = _impl::min(LhsTraits::digits, RhsTraits::digits);
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
};
template<class LhsTraits, class RhsTraits>
struct policy<_impl::bitwise_xor_op, LhsTraits, RhsTraits> {
static constexpr int digits = _impl::max(LhsTraits::digits, RhsTraits::digits);
static constexpr bool is_signed = LhsTraits::is_signed || RhsTraits::is_signed;
};
template<class OperationTag, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
class = enable_if_t<OperationTag::is_arithmetic>>
struct operate_params {
using lhs = elastic_integer<LhsDigits, LhsNarrowest>;
using rhs = elastic_integer<RhsDigits, RhsNarrowest>;
using lhs_traits = std::numeric_limits<lhs>;
using rhs_traits = std::numeric_limits<rhs>;
using policy = typename _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>;
static constexpr _digits_type narrowest_width = _impl::max(
digits<LhsNarrowest>::value + sg14::is_signed<LhsNarrowest>::value,
digits<RhsNarrowest>::value + sg14::is_signed<RhsNarrowest>::value);
using narrowest = set_digits_t<_impl::make_signed_t<rep_result, policy::is_signed>, narrowest_width-policy::is_signed>;
using result_type = elastic_integer<policy::digits, narrowest>;
};
template<class Operator, int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest,
bool Enable = Operator::is_arithmetic>
constexpr auto operate(
const elastic_integer<LhsDigits, LhsNarrowest>& lhs,
const elastic_integer<RhsDigits, RhsNarrowest>& rhs,
Operator op)
-> typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type
{
using result_type = typename operate_params<Operator, LhsDigits, LhsNarrowest, RhsDigits, RhsNarrowest>::result_type;
return result_type::from_data(
static_cast<typename result_type::rep>(op(
static_cast<result_type>(lhs).data(),
static_cast<result_type>(rhs).data())));
}
}
template<int RhsDigits, class RhsNarrowest>
constexpr auto operator-(const elastic_integer<RhsDigits, RhsNarrowest>& rhs)
-> elastic_integer<RhsDigits, typename make_signed<RhsNarrowest>::type>
{
using result_type = elastic_integer<RhsDigits, typename make_signed<RhsNarrowest>::type>;
return result_type::from_data(-static_cast<result_type>(rhs).data());
}
}
namespace std {
template<int LhsDigits, class LhsNarrowest, int RhsDigits, class RhsNarrowest>
struct common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, sg14::elastic_integer<RhsDigits, RhsNarrowest>> {
using type = sg14::elastic_integer<
sg14::_impl::max(LhsDigits, RhsDigits),
sg14::_impl::common_signedness_t<LhsNarrowest, RhsNarrowest>>;
};
template<int LhsDigits, class LhsNarrowest, class Rhs>
struct common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, Rhs>
: common_type<sg14::elastic_integer<LhsDigits, LhsNarrowest>, sg14::elastic_integer<std::numeric_limits<Rhs>::digits, Rhs>> {
};
template<class Lhs, int RhsDigits, class RhsNarrowest>
struct common_type<Lhs, sg14::elastic_integer<RhsDigits, RhsNarrowest>>
: common_type<sg14::elastic_integer<std::numeric_limits<Lhs>::digits, Lhs>, sg14::elastic_integer<RhsDigits, RhsNarrowest>> {
};
namespace _elastic_integer_impl {
template<class Rep, bool IsSigned>
struct lowest;
template<class Rep>
struct lowest<Rep, true> {
constexpr Rep operator()(const Rep& max) const noexcept
{
return -max;
}
};
template<class Rep>
struct lowest<Rep, false> {
constexpr Rep operator()(const Rep&) const noexcept
{
return 0;
}
};
};
template<int Digits, class Narrowest>
struct numeric_limits<sg14::elastic_integer<Digits, Narrowest>>
: numeric_limits<Narrowest> {
using _narrowest_numeric_limits = numeric_limits<Narrowest>;
using _value_type = sg14::elastic_integer<Digits, Narrowest>;
using _rep = typename _value_type::rep;
using _rep_numeric_limits = numeric_limits<_rep>;
static constexpr _rep _rep_max() noexcept
{
return _rep_numeric_limits::max() >> (_rep_numeric_limits::digits-digits);
}
static constexpr int 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_max());
}
static constexpr _value_type lowest() noexcept
{
return _elastic_integer_impl::lowest<_rep, _narrowest_numeric_limits::is_signed>()(_rep_max());
}
};
}
namespace sg14 {
template<class Rep = int, int Exponent = 0>
class fixed_point;
namespace _impl {
namespace fp {
template<int NumBits, class Enable = void>
struct float_of_size;
template<int NumBits>
struct float_of_size<NumBits, enable_if_t<NumBits <= sizeof(float)*8>> {
using type = float;
};
template<int NumBits>
struct float_of_size<NumBits, enable_if_t<sizeof(float)*8 < NumBits && NumBits <= sizeof(double)*8>> {
using type = double;
};
template<int NumBits>
struct float_of_size<NumBits, enable_if_t<sizeof(double)*8 < NumBits && NumBits <= sizeof(long double)*8>> {
using type = long double;
};
template<class T>
using float_of_same_size = typename float_of_size<digits<T>::value + is_signed<T>::value>::type;
}
}
template<class Rep, int Exponent>
class fixed_point
: public _impl::number_base<fixed_point<Rep, Exponent>, Rep> {
using _base = _impl::number_base<fixed_point<Rep, Exponent>, Rep>;
public:
using rep = Rep;
constexpr static int exponent = Exponent;
constexpr static int digits = std::numeric_limits<Rep>::digits;
constexpr static int integer_digits = digits+exponent;
constexpr static int fractional_digits = -exponent;
private:
constexpr fixed_point(rep r, int)
:_base(r)
{
}
public:
constexpr fixed_point() : _base() { }
template<class FromRep, int FromExponent>
constexpr fixed_point(const fixed_point<FromRep, FromExponent>& rhs)
: _base(fixed_point_to_rep(rhs))
{
}
template<class Integral, Integral Constant>
constexpr fixed_point(const std::integral_constant<Integral, Constant>&)
: fixed_point(fixed_point<Integral, 0>::from_data(Constant))
{
}
template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
constexpr fixed_point(const S& s)
: fixed_point(fixed_point<S, 0>::from_data(s))
{
}
template<class Integral, Integral Value, int Digits>
constexpr fixed_point(const_integer<Integral, Value, Digits, Exponent> ci)
: _base(ci << Exponent)
{
}
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
constexpr fixed_point(S s)
:_base(floating_point_to_rep(s))
{
}
template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
fixed_point& operator=(S s)
{
return operator=(fixed_point<S, 0>::from_data(s));
}
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
fixed_point& operator=(S s)
{
_base::operator=(floating_point_to_rep(s));
return *this;
}
template<class FromRep, int FromExponent>
fixed_point& operator=(const fixed_point<FromRep, FromExponent>& rhs)
{
_base::operator=(fixed_point_to_rep(rhs));
return *this;
}
constexpr operator bool() const
{
return static_cast<bool>(_base::data());
}
template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> Dummy = 0>
constexpr operator S() const
{
return rep_to_integral<S>(_base::data());
}
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
explicit constexpr operator S() const
{
return rep_to_floating_point<S>(_base::data());
}
static constexpr fixed_point from_data(rep const & r)
{
return fixed_point(r, 0);
}
private:
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy = 0>
static constexpr S one();
template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> 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);
};
template<class Rep, int Exponent>
constexpr int fixed_point<Rep, Exponent>::exponent;
template<class Rep, int Exponent>
constexpr int fixed_point<Rep, Exponent>::digits;
template<class Rep, int Exponent>
constexpr int fixed_point<Rep, Exponent>::integer_digits;
template<class Rep, int Exponent>
constexpr int fixed_point<Rep, Exponent>::fractional_digits;
namespace _impl {
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 {
};
template<int exp, class Output, class Input>
constexpr Output shift_left(Input i)
{
using larger = typename std::conditional<
digits<Input>::value<=digits<Output>::value,
Output, Input>::type;
return (exp>-std::numeric_limits<larger>::digits)
? static_cast<Output>(_impl::scale<larger>(i, 2, exp))
: Output{0};
}
namespace fp {
namespace type {
template<class S, int Exponent, enable_if_t<Exponent==0, int> 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,
enable_if_t<!(Exponent<=0) && (Exponent<8), int> 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, enable_if_t<(Exponent>=8), int> 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,
enable_if_t<!(Exponent>=0) && (Exponent>-8), int> 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, enable_if_t<(Exponent<=-8), int> 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);
}
}
}
}
template<class Rep, int Exponent>
template<class S, _impl::enable_if_t<std::is_floating_point<S>::value, int> Dummy>
constexpr S fixed_point<Rep, Exponent>::one()
{
return _impl::fp::type::pow2<S, -exponent>();
}
template<class Rep, int Exponent>
template<class S, _impl::enable_if_t<std::numeric_limits<S>::is_integer, int> 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());
}
}
namespace sg14 {
template<int IntegerDigits, int FractionalDigits = 0, class Narrowest = signed>
using make_fixed = fixed_point<
set_digits_t<Narrowest, IntegerDigits+FractionalDigits>,
-FractionalDigits>;
template<int IntegerDigits, int FractionalDigits = 0, class Narrowest = unsigned>
using make_ufixed = make_fixed<
IntegerDigits,
FractionalDigits,
typename make_unsigned<Narrowest>::type>;
}
namespace sg14 {
namespace _impl {
namespace fp {
namespace arithmetic {
struct raw_tag;
struct lean_tag;
struct wide_tag;
using ::sg14::fixed_point;
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");
};
template<class OperationTag, class ... Operands>
struct rep_op_exponent;
template<class Rhs>
struct rep_op_exponent<_impl::plus_op, Rhs> : public std::integral_constant<int, Rhs::exponent> {
};
template<class Rhs>
struct rep_op_exponent<_impl::minus_op, Rhs> : public std::integral_constant<int, Rhs::exponent> {
};
template<class Lhs, class Rhs>
struct rep_op_exponent<_impl::add_op, 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_op, 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_op, Lhs, Rhs> : public std::integral_constant<int,
Lhs::exponent+Rhs::exponent> {
};
template<class Lhs, class Rhs>
struct rep_op_exponent<_impl::divide_op, Lhs, Rhs> : public std::integral_constant<int,
Lhs::exponent-Rhs::exponent> {
};
template<class PolicyTag, class OperationTag, class Lhs, class Rhs>
struct result;
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>;
};
template<class OperationTag, class Lhs, class Rhs>
struct result<lean_tag, OperationTag, Lhs, Rhs> : result<raw_tag, OperationTag, Lhs, Rhs> {};
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>;
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 _digits_type sufficient_digits = sufficient_integer_digits+sufficient_fractional_digits;
static constexpr int result_digits = _impl::max(sufficient_digits, digits<rep_op_result>::value);
using rep_type = set_digits_t<rep_op_result, result_digits>;
using type = fixed_point<rep_type, -sufficient_fractional_digits>;
};
template<class Lhs, class Rhs>
struct result<wide_tag, _impl::multiply_op, Lhs, Rhs> {
using lhs_rep = typename Lhs::rep;
using rhs_rep = typename Rhs::rep;
using rep_op_result = _impl::op_result<_impl::multiply_op, 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;
using prewidened_result_rep = _impl::make_signed_t<rep_op_result, is_signed>;
using rep_type = set_digits_t<prewidened_result_rep, digits>;
static constexpr int rep_exponent = rep_op_exponent<_impl::multiply_op, Lhs, Rhs>::value;
using type = fixed_point<rep_type, rep_exponent>;
};
template<class Lhs, class Rhs>
struct result<wide_tag, _impl::divide_op, Lhs, Rhs> {
using lhs_rep = typename Lhs::rep;
using rhs_rep = typename Rhs::rep;
using rep_op_result = _impl::op_result<_impl::multiply_op, 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 necessary_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 promotion_digits = digits<rep_op_result>::value;
static constexpr int digits = _impl::max(necessary_digits, promotion_digits);
using prewidened_result_rep = _impl::make_signed_t<rep_op_result, is_signed>;
using rep_type = set_digits_t<prewidened_result_rep, digits>;
static constexpr int rep_exponent = -fractional_digits;
using type = fixed_point<rep_type, rep_exponent>;
};
template<class PolicyTag, class OperationTag, class Lhs, class Rhs>
struct intermediate;
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_op, Lhs, Rhs> {
using lhs_type = Lhs;
using rhs_type = Rhs;
};
template<class Lhs, class Rhs>
struct intermediate<lean_tag, _impl::divide_op, Lhs, Rhs> {
using lhs_type = Lhs;
using rhs_type = Rhs;
};
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_op, Lhs, Rhs> {
using _result = result<wide_tag, _impl::multiply_op, Lhs, Rhs>;
using result_rep = typename _result::rep_type;
using prewidened_result_rep = typename _result::prewidened_result_rep;
using rep_type = typename std::conditional<
digits<prewidened_result_rep>::value>=_result::digits,
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_op, Lhs, Rhs> {
using wide_result = result<wide_tag, _impl::divide_op, 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;
};
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;
};
}
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;
template<class PolicyTag, class Operation, class Lhs, class Rhs>
constexpr auto operate(const Lhs& lhs, const Rhs& rhs, Operation)
-> typename arithmetic::operate_params<PolicyTag, Operation, Lhs, Rhs>::result_type
{
using params = arithmetic::operate_params<PolicyTag, Operation, 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>(
Operation()(
static_cast<intermediate_lhs>(lhs).data(),
static_cast<intermediate_rhs>(rhs).data())));
};
}
}
}
namespace sg14 {
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());
}
template<class Lhs, class Rhs>
constexpr auto add(const Lhs& lhs, const Rhs& rhs)
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::add_tag))
{
return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::add_tag);
}
template<class Lhs, class Rhs>
constexpr auto subtract(const Lhs& lhs, const Rhs& rhs)
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::subtract_tag))
{
return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::subtract_tag);
}
template<class Lhs, class Rhs>
constexpr auto multiply(const Lhs& lhs, const Rhs& rhs)
-> decltype(_impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::multiply_tag))
{
return _impl::fp::operate<_impl::fp::named_function_tag>(lhs, rhs, _impl::multiply_tag);
}
template<class Lhs, class Rhs>
constexpr auto divide(const Lhs& lhs, const Rhs& rhs)
-> decltype(_impl::fp::operate<_impl::fp::division_named_function_tag>(lhs, rhs, _impl::divide_tag))
{
return _impl::fp::operate<_impl::fp::division_named_function_tag>(lhs, rhs, _impl::divide_tag);
}
}
namespace sg14 {
namespace _impl {
namespace fp {
namespace ct {
template<class Lhs, class Rhs, class _Enable = void>
struct common_type_mixed;
template<class LhsRep, int LhsExponent, class RhsInteger>
struct common_type_mixed<fixed_point
<LhsRep, LhsExponent>, RhsInteger, _impl::enable_if_t<std::numeric_limits<RhsInteger>::is_integer>> : std::common_type<
fixed_point<LhsRep, LhsExponent>, fixed_point<RhsInteger, 0>> {
};
template<class LhsRep, int LhsExponent, class Float>
struct common_type_mixed<
fixed_point<LhsRep, LhsExponent>, Float,
_impl::enable_if_t<std::is_floating_point<Float>::value>>
: std::common_type<_impl::fp::float_of_same_size<LhsRep>, Float> {
};
}
}
}
}
namespace std {
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>;
};
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;
};
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;
};
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;
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>;
};
}
namespace sg14 {
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());
}
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>(lhs, rhs, _impl::add_tag))
{
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag>(lhs, rhs, _impl::add_tag);
}
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>(lhs, rhs, _impl::subtract_tag))
{
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag>(lhs, rhs, _impl::subtract_tag);
}
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>(lhs, rhs, _impl::multiply_tag))
{
return _impl::fp::operate<_impl::fp::arithmetic_operator_tag>(lhs, rhs, _impl::multiply_tag);
}
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>(lhs, rhs, _impl::divide_tag))
{
return _impl::fp::operate<_impl::fp::division_arithmetic_operator_tag>(lhs, rhs, _impl::divide_tag);
}
template<class Rep, int Exponent>
constexpr auto operator==(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()==rhs.data())
{
return lhs.data()==rhs.data();
}
template<class Rep, int Exponent>
constexpr auto operator!=(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()!=rhs.data())
{
return lhs.data()!=rhs.data();
}
template<class Rep, int Exponent>
constexpr auto operator>(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()>rhs.data())
{
return lhs.data()>rhs.data();
}
template<class Rep, int Exponent>
constexpr auto operator<(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()<rhs.data())
{
return lhs.data()<rhs.data();
}
template<class Rep, int Exponent>
constexpr auto operator>=(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()>=rhs.data())
{
return lhs.data()>=rhs.data();
}
template<class Rep, int Exponent>
constexpr auto operator<=(
const fixed_point <Rep, Exponent>& lhs,
const fixed_point <Rep, Exponent>& rhs)
-> decltype(lhs.data()<=rhs.data())
{
return lhs.data()<=rhs.data();
}
namespace _fixed_point_operators_impl {
template<class Lhs, class Rhs>
constexpr bool is_heterogeneous() {
return (!std::is_same<Lhs, Rhs>::value) &&
(_impl::is_fixed_point<Lhs>::value || _impl::is_fixed_point<Rhs>::value);
}
}
namespace _impl {
template<
class Operator, class Lhs, class Rhs,
class = _impl::enable_if_t<Operator::is_comparison && sg14::_fixed_point_operators_impl::is_heterogeneous<Lhs, Rhs>()>>
constexpr auto operate(const Lhs& lhs, const Rhs& rhs, Operator op)
-> decltype(op(static_cast<_impl::common_type_t<Lhs, Rhs>>(lhs), static_cast<_impl::common_type_t<Lhs, Rhs>>(rhs)))
{
return op(static_cast<_impl::common_type_t<Lhs, Rhs>>(lhs), static_cast<_impl::common_type_t<Lhs, Rhs>>(rhs));
};
}
template<
class LhsRep, int LhsExponent,
class RhsInteger,
typename = _impl::enable_if_t<std::numeric_limits<RhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<RhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<RhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<RhsInteger>::is_integer>>
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 LhsInteger,
class RhsRep, int RhsExponent,
typename = _impl::enable_if_t<std::numeric_limits<LhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<LhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<LhsInteger>::is_integer>>
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 = _impl::enable_if_t<std::numeric_limits<LhsInteger>::is_integer>>
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 LhsRep, int LhsExponent, class RhsFloat, typename = _impl::enable_if_t<std::is_floating_point<RhsFloat>::value>>
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 = _impl::enable_if_t <std::is_floating_point<RhsFloat>::value>>
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>,
_impl::enable_if_t<std::is_floating_point<RhsFloat>::value, RhsFloat>>
{
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>,
_impl::enable_if_t<std::is_floating_point<RhsFloat>::value, RhsFloat>>
{
using result_type = _impl::common_type_t<fixed_point<LhsRep, LhsExponent>, RhsFloat>;
return static_cast<result_type>(lhs)/rhs;
}
template<class LhsFloat, class RhsRep, int RhsExponent, typename = _impl::enable_if_t <std::is_floating_point<LhsFloat>::value>>
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 = _impl::enable_if_t <std::is_floating_point<LhsFloat>::value>>
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 <_impl::enable_if_t<std::is_floating_point<LhsFloat>::value, LhsFloat>, 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 <_impl::enable_if_t<std::is_floating_point<LhsFloat>::value, LhsFloat>, 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 LhsExponent, class Rhs>
constexpr auto operator<<(const fixed_point<LhsRep, LhsExponent>& lhs, const Rhs& rhs)
-> decltype(_impl::from_rep<fixed_point<decltype(lhs.data() << rhs), LhsExponent>>(lhs.data() << rhs))
{
return _impl::from_rep<fixed_point<decltype(lhs.data() << rhs), LhsExponent>>(lhs.data() << rhs);
}
template<class LhsRep, int LhsExponent, class Rhs>
constexpr auto operator>>(const fixed_point<LhsRep, LhsExponent>& lhs, const Rhs& rhs)
-> decltype(_impl::from_rep<fixed_point<decltype(lhs.data() >> rhs), LhsExponent>>(lhs.data() >> rhs))
{
return _impl::from_rep<fixed_point<decltype(lhs.data() >> rhs), LhsExponent>>(lhs.data() >> rhs);
}
template<class LhsRep, int LhsExponent, class RhsIntegral, RhsIntegral RhsValue>
constexpr fixed_point<LhsRep, LhsExponent+RhsValue>
operator<<(const fixed_point<LhsRep, LhsExponent>& lhs, const_integer<RhsIntegral, RhsValue>)
{
return fixed_point<LhsRep, LhsExponent+RhsValue>::from_data(lhs.data());
}
template<class LhsRep, int LhsExponent, class RhsIntegral, RhsIntegral RhsValue>
constexpr fixed_point<LhsRep, LhsExponent-RhsValue>
operator>>(const fixed_point<LhsRep, LhsExponent>& lhs, const_integer<RhsIntegral, RhsValue>)
{
return fixed_point<LhsRep, LhsExponent-RhsValue>::from_data(lhs.data());
}
template<class LhsRep, int LhsExponent, class RhsIntegral, RhsIntegral RhsValue>
constexpr fixed_point<LhsRep, LhsExponent+RhsValue>
operator<<(const fixed_point<LhsRep, LhsExponent>& lhs, std::integral_constant<RhsIntegral, RhsValue>)
{
return fixed_point<LhsRep, LhsExponent+RhsValue>::from_data(lhs.data());
}
template<class LhsRep, int LhsExponent, class RhsIntegral, RhsIntegral RhsValue>
constexpr fixed_point<LhsRep, LhsExponent-RhsValue>
operator>>(const fixed_point<LhsRep, LhsExponent>& lhs, std::integral_constant<RhsIntegral, RhsValue>)
{
return fixed_point<LhsRep, LhsExponent-RhsValue>::from_data(lhs.data());
}
}
namespace sg14 {
namespace _impl {
template<class Rep, int Exponent>
struct get_rep<fixed_point<Rep, Exponent>> {
using type = Rep;
};
template<class OldRep, int Exponent, class NewRep>
struct set_rep<fixed_point<OldRep, Exponent>, NewRep> {
using type = fixed_point<NewRep, Exponent>;
};
}
template<class Rep, int Exponent>
struct digits<fixed_point<Rep, Exponent>> : digits<Rep> {
};
template<class Rep, int Exponent, _digits_type MinNumBits>
struct set_digits<fixed_point<Rep, Exponent>, MinNumBits> {
using type = fixed_point<set_digits_t<Rep, MinNumBits>, Exponent>;
};
template<class Rep, int Exponent, class Value>
struct from_value<fixed_point<Rep, Exponent>, Value> {
using type = fixed_point<Value>;
};
template<class Rep, int Exponent>
constexpr auto abs(const fixed_point<Rep, Exponent>& x) noexcept
-> decltype(-x)
{
return (x >= 0) ? static_cast<decltype(-x)>(x) : -x;
}
namespace _impl {
namespace fp {
namespace extras {
template<class Rep>
constexpr Rep sqrt_bit(Rep n, Rep bit)
{
return (bit>n) ? sqrt_bit<Rep>(n, bit >> 2) : bit;
}
template<class Rep>
constexpr Rep sqrt_bit(Rep n)
{
return sqrt_bit<Rep>(n, Rep(1) << ((digits<Rep>::value + is_signed<Rep>::value) - 2));
}
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});
}
}
}
}
template<class Rep, int Exponent>
constexpr fixed_point <Rep, Exponent>
sqrt(const fixed_point <Rep, Exponent>& x)
{
using widened_type = fixed_point<set_digits_t<Rep, digits<Rep>::value*2>, Exponent*2>;
return
(x<fixed_point<Rep, Exponent>(0))
? throw std::invalid_argument("cannot represent square root of negative value") :
fixed_point<Rep, Exponent>::from_data(
static_cast<Rep>(_impl::fp::extras::sqrt_solve1(widened_type{x}.data())));
}
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);
}
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 {
template<class Rep, int Exponent>
struct numeric_limits<sg14::fixed_point<Rep, Exponent>>
: std::numeric_limits<sg14::_impl::number_base<sg14::fixed_point<Rep, Exponent>, Rep>> {
using _value_type = sg14::fixed_point<Rep, Exponent>;
using _rep = typename _value_type::rep;
using _rep_numeric_limits = numeric_limits<_rep>;
static constexpr _value_type min() noexcept
{
return _value_type::from_data(_rep{1});
}
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(_rep{1});
}
static constexpr _value_type round_error() noexcept
{
return static_cast<_value_type>(.5);
}
static constexpr _value_type infinity() noexcept
{
return _value_type::from_data(_rep{0});
}
static constexpr _value_type quiet_NaN() noexcept
{
return _value_type::from_data(_rep{0});
}
static constexpr _value_type signaling_NaN() noexcept
{
return _value_type::from_data(_rep{0});
}
static constexpr _value_type denorm_min() noexcept
{
return _value_type::from_data(_rep{1});
}
};
}
namespace sg14 {
template<int IntegerDigits, int FractionalDigits = 0, class Narrowest = signed>
using elastic_fixed_point = fixed_point<elastic_integer<IntegerDigits+FractionalDigits, Narrowest>, -FractionalDigits>;
template<
typename Narrowest = int,
typename Integral = int,
Integral Value = 0>
constexpr elastic_fixed_point<_impl::max(_impl::used_bits_symmetric(Value), 1), -trailing_bits(Value), Narrowest>
make_elastic_fixed_point(const_integer<Integral, Value> = const_integer<Integral, Value>{})
{
return Value;
}
template<class Narrowest = int, class Integral = int>
constexpr elastic_fixed_point<std::numeric_limits<Integral>::digits, 0, Narrowest>
make_elastic_fixed_point(Integral value)
{
return {value};
}
namespace literals {
template<char... Digits>
constexpr auto operator "" _elastic()
-> decltype(make_elastic_fixed_point<int, std::intmax_t, _const_integer_impl::digits_to_integral<Digits...>::value>()) {
return make_elastic_fixed_point<int, std::intmax_t, _const_integer_impl::digits_to_integral<Digits...>::value>();
}
}
}
namespace sg14 {
static constexpr struct native_overflow_tag {
} native_overflow{};
static constexpr struct throwing_overflow_tag {
} throwing_overflow{};
static constexpr struct saturated_overflow_tag {
} saturated_overflow{};
namespace _overflow_impl {
template<class Result>
constexpr Result return_if(bool condition, const Result& value, const char* )
{
return condition ? value : throw std::overflow_error("");
}
template<class T>
struct positive_digits : public std::integral_constant<int, std::numeric_limits<T>::digits> {
};
template<class T>
struct negative_digits
: public std::integral_constant<int, std::is_signed<T>::value ? std::numeric_limits<T>::digits : 0> {
};
template<
class Destination, class Source,
_impl::enable_if_t<!(positive_digits<Destination>::value<positive_digits<Source>::value), int> dummy = 0>
constexpr bool is_positive_overflow(Source const&)
{
return false;
}
template<
class Destination, class Source,
_impl::enable_if_t<(positive_digits<Destination>::value<positive_digits<Source>::value), int> dummy = 0>
constexpr bool is_positive_overflow(Source const& source)
{
return source>static_cast<Source>(std::numeric_limits<Destination>::max());
}
template<
class Destination, class Source,
_impl::enable_if_t<!(negative_digits<Destination>::value<negative_digits<Source>::value), int> dummy = 0>
constexpr bool is_negative_overflow(Source const&)
{
return false;
}
template<
class Destination, class Source,
_impl::enable_if_t<(negative_digits<Destination>::value<negative_digits<Source>::value), int> dummy = 0>
constexpr bool is_negative_overflow(Source const& source)
{
return source<static_cast<Source>(std::numeric_limits<Destination>::lowest());
}
template<class OverflowTag, class Operator, class Enable = void>
struct operate;
}
template<class Result, class Input>
constexpr Result convert(native_overflow_tag, const Input& rhs)
{
return static_cast<Result>(rhs);
}
template<class Result, class Input>
constexpr Result convert(throwing_overflow_tag, const Input& rhs)
{
return _impl::encompasses<Result, Input>::value
? static_cast<Result>(rhs)
: _overflow_impl::return_if(
!_overflow_impl::is_positive_overflow<Result>(rhs),
_overflow_impl::return_if(
!_overflow_impl::is_negative_overflow<Result>(rhs),
static_cast<Result>(rhs),
"negative overflow in conversion"),
"positive overflow in conversion");
}
template<class Result, class Input>
constexpr Result convert(saturated_overflow_tag, const Input& rhs)
{
using numeric_limits = std::numeric_limits<Result>;
return !_impl::encompasses<Result, Input>::value
? _overflow_impl::is_positive_overflow<Result>(rhs)
? numeric_limits::max()
: _overflow_impl::is_negative_overflow<Result>(rhs)
? numeric_limits::lowest()
: static_cast<Result>(rhs)
: static_cast<Result>(rhs);
}
namespace _overflow_impl {
template<>
struct operate<native_overflow_tag, _impl::add_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs+rhs)
{
return lhs+rhs;
}
};
template<>
struct operate<throwing_overflow_tag, _impl::add_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs+rhs)
{
using result_type = decltype(lhs+rhs);
using numeric_limits = std::numeric_limits<result_type>;
return _overflow_impl::return_if(
!((rhs>=_impl::from_rep<Rhs>(0))
? (lhs>numeric_limits::max()-rhs)
: (lhs<numeric_limits::lowest()-rhs)),
lhs+rhs,
"overflow in addition");
}
};
template<>
struct operate<saturated_overflow_tag, _impl::add_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> _impl::op_result<_impl::add_op, Lhs, Rhs>
{
using result_type = decltype(lhs+rhs);
using numeric_limits = std::numeric_limits<result_type>;
return (rhs>0)
? (lhs>numeric_limits::max()-rhs) ? numeric_limits::max() : lhs+rhs
: (lhs<numeric_limits::lowest()-rhs) ? numeric_limits::lowest() : lhs+rhs;
}
};
}
template<class OverflowTag, class Lhs, class Rhs>
constexpr auto add(OverflowTag, const Lhs& lhs, const Rhs& rhs)
-> decltype(lhs+rhs)
{
return for_rep<decltype(lhs+rhs)>(_overflow_impl::operate<OverflowTag, _impl::add_op>(), lhs, rhs);
}
namespace _overflow_impl {
template<>
struct operate<native_overflow_tag, _impl::subtract_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs-rhs)
{
return lhs-rhs;
}
};
template<>
struct operate<throwing_overflow_tag, _impl::subtract_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs-rhs)
{
using result_type = decltype(lhs-rhs);
using numeric_limits = std::numeric_limits<result_type>;
return _overflow_impl::return_if(
(rhs<_impl::from_rep<Rhs>(0))
? (lhs<=numeric_limits::max()+rhs)
: (lhs>=numeric_limits::lowest()+rhs),
lhs-rhs,
"positive overflow in subtraction");
}
};
template<>
struct operate<saturated_overflow_tag, _impl::subtract_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> _impl::op_result<_impl::subtract_op, Lhs, Rhs>
{
using result_type = decltype(lhs-rhs);
using numeric_limits = std::numeric_limits<result_type>;
return (rhs<0)
? (lhs>numeric_limits::max()+rhs) ? numeric_limits::max() : lhs-rhs
: (lhs<numeric_limits::lowest()+rhs) ? numeric_limits::lowest() : lhs-rhs;
}
};
}
template<class OverflowTag, class Lhs, class Rhs>
constexpr auto subtract(OverflowTag, const Lhs& lhs, const Rhs& rhs)
-> decltype(lhs-rhs)
{
return for_rep<decltype(lhs-rhs)>(_overflow_impl::operate<OverflowTag, _impl::subtract_op>(), lhs, rhs);
}
namespace _overflow_impl {
template<>
struct operate<native_overflow_tag, _impl::multiply_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs*rhs)
{
return lhs*rhs;
}
};
template<class Lhs, class Rhs>
constexpr bool is_multiply_overflow(const Lhs& lhs, const Rhs& rhs)
{
using result_nl = std::numeric_limits<decltype(lhs*rhs)>;
return lhs && rhs && ((lhs>Lhs{})
? ((rhs>Rhs{}) ? (result_nl::max()/rhs) : (result_nl::lowest()/rhs))<lhs
: ((rhs>Rhs{}) ? (result_nl::lowest()/rhs) : (result_nl::max()/rhs))>lhs);
}
template<>
struct operate<throwing_overflow_tag, _impl::multiply_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs*rhs)
{
return _overflow_impl::return_if(
!is_multiply_overflow(lhs, rhs),
lhs*rhs, "overflow in multiplication");
}
};
template<>
struct operate<saturated_overflow_tag, _impl::multiply_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> _impl::op_result<_impl::multiply_op, Lhs, Rhs>
{
using result_type = decltype(lhs*rhs);
return is_multiply_overflow(lhs, rhs)
? ((lhs>0) ^ (rhs>0))
? std::numeric_limits<result_type>::lowest()
: std::numeric_limits<result_type>::max()
: lhs*rhs;
}
};
}
template<class OverflowTag, class Lhs, class Rhs>
constexpr auto multiply(OverflowTag, const Lhs& lhs, const Rhs& rhs)
-> decltype(lhs*rhs)
{
return for_rep<decltype(lhs*rhs)>(_overflow_impl::operate<OverflowTag, _impl::multiply_op>(), lhs, rhs);
}
namespace _overflow_impl {
template<class OverflowTag>
struct operate<OverflowTag, _impl::divide_op> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(lhs/rhs)
{
return lhs/rhs;
}
};
}
namespace _overflow_impl {
template<class Operator>
struct operate<native_overflow_tag, Operator,
_impl::enable_if_t<Operator::is_comparison>> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> decltype(Operator()(lhs, rhs))
{
return Operator()(lhs, rhs);
}
};
template<class Operator>
struct operate<throwing_overflow_tag, Operator,
_impl::enable_if_t<Operator::is_comparison>> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> _impl::op_result<Operator, Lhs, Rhs>
{
return Operator()(lhs, rhs);
}
};
template<class Operator>
struct operate<saturated_overflow_tag, Operator,
_impl::enable_if_t<Operator::is_comparison>> {
template<class Lhs, class Rhs>
constexpr auto operator()(const Lhs& lhs, const Rhs& rhs) const
-> _impl::op_result<Operator, Lhs, Rhs>
{
using converted = decltype(lhs | rhs);
return Operator()(
convert<converted>(saturated_overflow, lhs),
convert<converted>(saturated_overflow, rhs));
}
};
}
}
namespace sg14 {
struct closest_rounding_policy {
template<class To, class From>
static constexpr To convert(const From& from)
{
return static_cast<To>(std::intmax_t(from+((from>=0) ? .5 : -.5)));
}
};
template<class Rep = int, class RoundingPolicy = closest_rounding_policy>
class precise_integer : public _impl::number_base<precise_integer<Rep, RoundingPolicy>, Rep> {
using super = _impl::number_base<precise_integer<Rep, RoundingPolicy>, Rep>;
public:
using rounding = RoundingPolicy;
constexpr precise_integer() = default;
template<class T, _impl::enable_if_t<std::numeric_limits<T>::is_integer, int> Dummy = 0>
constexpr precise_integer(const T& v)
: super(static_cast<Rep>(v)) { }
template<class T, _impl::enable_if_t<!std::numeric_limits<T>::is_integer, int> Dummy = 0>
constexpr precise_integer(const T& v)
: super(rounding::template convert<Rep>(v)) { }
template<class T>
constexpr explicit operator T() const
{
return static_cast<T>(super::data());
}
};
template<class Rep, class RoundingTag>
struct digits<precise_integer<Rep, RoundingTag>> : digits<Rep> {
};
template<class Rep, class RoundingTag, _digits_type MinNumBits>
struct set_digits<precise_integer<Rep, RoundingTag>, MinNumBits> {
using type = precise_integer<set_digits_t<Rep, MinNumBits>, RoundingTag>;
};
namespace _impl {
template<class Rep, class RoundingTag>
struct get_rep<precise_integer<Rep, RoundingTag>> {
using type = Rep;
};
template<class OldRep, class RoundingTag, class NewRep>
struct set_rep<precise_integer<OldRep, RoundingTag>, NewRep> {
using type = precise_integer<NewRep, RoundingTag>;
};
}
template<class Rep, class RoundingTag, class Value>
struct from_value<precise_integer<Rep, RoundingTag>, Value> {
using type = precise_integer<Value, RoundingTag>;
};
template<class Rep, class RoundingTag>
struct scale<precise_integer<Rep, RoundingTag>>
: scale<_impl::number_base<precise_integer<Rep, RoundingTag>, Rep>> {
};
namespace _precise_integer_impl {
template<class T>
struct is_precise_integer : std::false_type {
};
template<class Rep, class RoundingPolicy>
struct is_precise_integer<precise_integer<Rep, RoundingPolicy>> : std::true_type {
};
}
namespace _impl {
template<class Operator, class RoundingPolicy, class LhsRep, class RhsRep, class = enable_if_t<Operator::is_arithmetic>>
constexpr auto operate_common_policy(
const precise_integer<LhsRep, RoundingPolicy>& lhs,
const precise_integer<RhsRep, RoundingPolicy>& rhs)
-> decltype(from_rep<precise_integer<op_result<Operator, LhsRep, RhsRep>, RoundingPolicy>>(Operator()(lhs.data(), rhs.data())))
{
using result_type = precise_integer<op_result<Operator, LhsRep, RhsRep>, RoundingPolicy>;
return from_rep<result_type>(Operator()(lhs.data(), rhs.data()));
}
template<class Operator, class RoundingPolicy, class LhsRep, class RhsRep, enable_if_t<Operator::is_comparison, int> = 0>
constexpr auto operate_common_policy(
const precise_integer<LhsRep, RoundingPolicy>& lhs,
const precise_integer<RhsRep, RoundingPolicy>& rhs)
-> decltype(Operator()(lhs.data(), rhs.data()))
{
return Operator()(lhs.data(), rhs.data());
}
template<class Operator, class LhsRep, class LhsRoundingPolicy, class RhsRep, class RhsRoundingPolicy>
constexpr auto operate(
const precise_integer<LhsRep, LhsRoundingPolicy>& lhs,
const precise_integer<RhsRep, RhsRoundingPolicy>& rhs,
Operator)
-> decltype(operate_common_policy<Operator, common_type_t<LhsRoundingPolicy, RhsRoundingPolicy>>(lhs, rhs))
{
return operate_common_policy<Operator, common_type_t<LhsRoundingPolicy, RhsRoundingPolicy>>(lhs, rhs);
}
}
template<class LhsRep, class LhsRoundingPolicy, class RhsInteger>
constexpr auto operator<<(
const precise_integer<LhsRep, LhsRoundingPolicy>& lhs,
const RhsInteger& rhs)
-> decltype(from_rep<precise_integer<decltype(_impl::to_rep(lhs) << rhs), LhsRoundingPolicy>>(_impl::to_rep(lhs) << rhs))
{
return from_rep<precise_integer<
decltype(_impl::to_rep(lhs) << rhs),
LhsRoundingPolicy>>(_impl::to_rep(lhs) << rhs);
}
}
namespace std {
template<class Rep, class RoundingPolicy>
struct numeric_limits<sg14::precise_integer<Rep, RoundingPolicy>>
: numeric_limits<sg14::_impl::number_base<sg14::precise_integer<Rep, RoundingPolicy>, Rep>> {};
}
namespace sg14 {
template<class Rep, class OverflowTag>
class safe_integer;
namespace _integer_impl {
template<class T>
struct is_safe_integer
: std::false_type {
};
template<class Rep, class OverflowTag>
struct is_safe_integer<safe_integer<Rep, OverflowTag>>
: std::true_type {
};
template<class Lhs, class Rhs>
struct are_integer_class_operands {
static constexpr int integer_class = is_safe_integer<Lhs>::value + is_safe_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);
};
template<class, class, class = void>
struct common_type;
template<class LhsRep, class RhsRep, class OverflowTag>
struct common_type<
safe_integer<LhsRep, OverflowTag>,
safe_integer<RhsRep, OverflowTag>> {
using type = safe_integer<
typename std::common_type<LhsRep, RhsRep>::type,
OverflowTag>;
};
template<class LhsRep, class LhsOverflowTag, class RhsInteger>
struct common_type<
safe_integer<LhsRep, LhsOverflowTag>, RhsInteger,
_impl::enable_if_t<
!_integer_impl::is_safe_integer<RhsInteger>::value && std::is_integral<RhsInteger>::value>> {
using type = typename sg14::safe_integer<typename std::common_type<LhsRep, RhsInteger>::type, LhsOverflowTag>;
};
template<class LhsRep, class LhsOverflowTag, class Float>
struct common_type<
safe_integer<LhsRep, LhsOverflowTag>, Float,
_impl::enable_if_t<std::is_floating_point<Float>::value>> {
using type = typename std::common_type<LhsRep, Float>::type;
};
template<class Lhs, class RhsRep, class RhsOverflowTag>
struct common_type<Lhs, safe_integer<RhsRep, RhsOverflowTag>>
: common_type<safe_integer<RhsRep, RhsOverflowTag>, Lhs> {
};
}
template<class Rep = int, class OverflowTag = throwing_overflow_tag>
class safe_integer : public _impl::number_base<safe_integer<Rep, OverflowTag>, Rep> {
using _base = _impl::number_base<safe_integer<Rep, OverflowTag>, Rep>;
public:
using rep = Rep;
using overflow_tag = OverflowTag;
constexpr safe_integer() = delete;
template<class RhsRep, class RhsOverflowTag>
constexpr safe_integer(const safe_integer<RhsRep, RhsOverflowTag>& rhs)
:safe_integer(rhs.data())
{
}
template<class Rhs, _impl::enable_if_t<!_integer_impl::is_safe_integer<Rhs>::value, int> dummy = 0>
constexpr safe_integer(const Rhs& rhs)
:_base(convert<rep>(overflow_tag{}, rhs))
{
}
template<class Integral, Integral Value, int Digits, int Exponent>
constexpr safe_integer(const_integer<Integral, Value, Digits, Exponent>)
: _base(static_cast<rep>(Value))
{
static_assert(Value <= std::numeric_limits<rep>::max(), "initialization by out-of-range value");
static_assert(!std::numeric_limits<Integral>::is_signed || Value >= std::numeric_limits<rep>::lowest(), "initialization by out-of-range value");
}
template<class T>
constexpr explicit operator T() const
{
return static_cast<T>(_base::data());
}
};
namespace _impl {
template<class Rep, class OverflowTag>
struct get_rep<safe_integer<Rep, OverflowTag>> {
using type = Rep;
};
template<class OldRep, class OverflowTag, class NewRep>
struct set_rep<safe_integer<OldRep, OverflowTag>, NewRep> {
using type = safe_integer<NewRep, OverflowTag>;
};
}
template<class Rep, class OverflowTag>
struct digits<safe_integer<Rep, OverflowTag>> : digits<Rep> {
};
template<class Rep, class OverflowTag, _digits_type MinNumBits>
struct set_digits<safe_integer<Rep, OverflowTag>, MinNumBits> {
using type = safe_integer<set_digits_t<Rep, MinNumBits>, OverflowTag>;
};
template<class Rep, class OverflowTag, class Value>
struct from_value<safe_integer<Rep, OverflowTag>, Value> {
using type = safe_integer<Value, OverflowTag>;
};
template<class Rep, class OverflowTag>
struct scale<safe_integer<Rep, OverflowTag>> {
using value_type = safe_integer<Rep, OverflowTag>;
constexpr auto operator()(const value_type &i, int base, int exp) const
-> decltype(_impl::to_rep(i) * _num_traits_impl::pow<value_type>(base, exp)) {
return (exp < 0)
? _impl::to_rep(i) / _num_traits_impl::pow<value_type>(base, -exp)
: _impl::to_rep(i) * _num_traits_impl::pow<value_type>(base, exp);
}
};
template<class OverflowTag, class Rep>
constexpr auto make_safe_integer(Rep const& value)
-> safe_integer<Rep, OverflowTag>
{
return value;
}
namespace _impl {
template<class OverflowTag, class OperatorTag, class LhsRep, class RhsRep, class = enable_if_t<OperatorTag::is_arithmetic>>
constexpr auto operate_common_policy(
OverflowTag,
OperatorTag,
const safe_integer<LhsRep, OverflowTag>& lhs,
const safe_integer<RhsRep, OverflowTag>& rhs)
-> decltype(make_safe_integer<OverflowTag>(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data())))
{
return make_safe_integer<OverflowTag>(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data()));
}
template<class OverflowTag, class OperatorTag, class LhsRep, class RhsRep, class = enable_if_t<OperatorTag::is_comparison>>
constexpr auto operate_common_policy(
OverflowTag,
OperatorTag,
const safe_integer<LhsRep, OverflowTag>& lhs,
const safe_integer<RhsRep, OverflowTag>& rhs)
-> decltype(_overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data()))
{
return _overflow_impl::operate<OverflowTag, OperatorTag>()(lhs.data(), rhs.data());
}
template<class OperatorTag, class LhsRep, class LhsPolicy, class RhsRep, class RhsPolicy>
constexpr auto operate(
const safe_integer<LhsRep, LhsPolicy>& lhs,
const safe_integer<RhsRep, RhsPolicy>& rhs,
OperatorTag operator_tag)
-> decltype(operate_common_policy(common_type_t<LhsPolicy, RhsPolicy>{}, operator_tag, lhs, rhs))
{
return operate_common_policy(common_type_t<LhsPolicy, RhsPolicy>{}, operator_tag, lhs, rhs);
}
}
template <class LhsRep, class LhsOverflowTag, class RhsRep, class RhsOverflowTag> constexpr auto operator >> (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) -> safe_integer<LhsRep, LhsOverflowTag> { return lhs.data() >> rhs.data(); } template <class Lhs, class RhsRep, class RhsOverflowTag, _impl::enable_if_t<std::is_fundamental<Lhs>::value, int> dummy = 0> constexpr auto operator >> (const Lhs& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) -> Lhs { return lhs >> rhs.data(); } template <class LhsRep, class LhsOverflowTag, class Rhs, _impl::enable_if_t<std::is_fundamental<Rhs>::value, int> dummy = 0> constexpr auto operator >> (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const Rhs& rhs) -> safe_integer<LhsRep, LhsOverflowTag> { return safe_integer<LhsRep, LhsOverflowTag>(lhs.data() >> rhs); };
template <class LhsRep, class LhsOverflowTag, class RhsRep, class RhsOverflowTag> constexpr auto operator << (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) -> safe_integer<LhsRep, LhsOverflowTag> { return lhs.data() << rhs.data(); } template <class Lhs, class RhsRep, class RhsOverflowTag, _impl::enable_if_t<std::is_fundamental<Lhs>::value, int> dummy = 0> constexpr auto operator << (const Lhs& lhs, const safe_integer<RhsRep, RhsOverflowTag>& rhs) -> Lhs { return lhs << rhs.data(); } template <class LhsRep, class LhsOverflowTag, class Rhs, _impl::enable_if_t<std::is_fundamental<Rhs>::value, int> dummy = 0> constexpr auto operator << (const safe_integer<LhsRep, LhsOverflowTag>& lhs, const Rhs& rhs) -> safe_integer<LhsRep, LhsOverflowTag> { return safe_integer<LhsRep, LhsOverflowTag>(lhs.data() << rhs); };
}
namespace std {
template<
class Lhs,
class RhsRep, class RhsOverflowTag>
struct common_type<
Lhs,
sg14::safe_integer<RhsRep, RhsOverflowTag>>
: sg14::_integer_impl::common_type<
Lhs,
sg14::safe_integer<RhsRep, RhsOverflowTag>> {
};
template<
class LhsRep, class LhsOverflowTag,
class Rhs>
struct common_type<
sg14::safe_integer<LhsRep, LhsOverflowTag>,
Rhs>
: sg14::_integer_impl::common_type<
sg14::safe_integer<LhsRep, LhsOverflowTag>,
Rhs> {
};
template<
class LhsRep, class LhsOverflowTag,
class RhsRep, int RhsExponent>
struct common_type<
sg14::safe_integer<LhsRep, LhsOverflowTag>,
sg14::fixed_point<RhsRep, RhsExponent>>
: std::common_type<
sg14::fixed_point<sg14::safe_integer<LhsRep, LhsOverflowTag>, 0>,
sg14::fixed_point<RhsRep, RhsExponent>> {
};
template<
class LhsRep, int LhsExponent,
class RhsRep, class RhsOverflowTag>
struct common_type<
sg14::fixed_point<LhsRep, LhsExponent>,
sg14::safe_integer<RhsRep, RhsOverflowTag>>
: std::common_type<
sg14::fixed_point<LhsRep, LhsExponent>,
sg14::fixed_point<sg14::safe_integer<RhsRep, RhsOverflowTag>, 0>> {
};
template<
class LhsRep, class LhsOverflowTag,
class RhsRep, class RhsOverflowTag>
struct common_type<
sg14::safe_integer<LhsRep, LhsOverflowTag>,
sg14::safe_integer<RhsRep, RhsOverflowTag>>
: sg14::_integer_impl::common_type<
sg14::safe_integer<LhsRep, LhsOverflowTag>,
sg14::safe_integer<RhsRep, RhsOverflowTag>> {
};
template<class Rep, class OverflowTag>
struct numeric_limits<sg14::safe_integer<Rep, OverflowTag>>
: numeric_limits<sg14::_impl::number_base<sg14::safe_integer<Rep, OverflowTag>, Rep>> {};
}
#endif // SG14_FIXED_POINT_SINGLE_HEADER)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment