/////////////////////////////////////////////////////////////////////////////// // Copyright 2011 John Maddock. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_MATH_MP_TOMMATH_BACKEND_HPP #define BOOST_MATH_MP_TOMMATH_BACKEND_HPP #include #include #include #include #include #include #include #include #include #include namespace boost{ namespace multiprecision{ namespace backends{ namespace detail{ inline void check_tommath_result(unsigned v) { if(v != MP_OKAY) { BOOST_THROW_EXCEPTION(std::runtime_error(mp_error_to_string(v))); } } } struct tommath_int; void eval_multiply(tommath_int& t, const tommath_int& o); void eval_add(tommath_int& t, const tommath_int& o); struct tommath_int { typedef mpl::list signed_types; typedef mpl::list unsigned_types; typedef mpl::list float_types; tommath_int() { detail::check_tommath_result(mp_init(&m_data)); } tommath_int(const tommath_int& o) { detail::check_tommath_result(mp_init_copy(&m_data, const_cast< ::mp_int*>(&o.m_data))); } #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES tommath_int(tommath_int&& o) BOOST_NOEXCEPT { m_data = o.m_data; o.m_data.dp = 0; } tommath_int& operator = (tommath_int&& o) { mp_exch(&m_data, &o.m_data); return *this; } #endif tommath_int& operator = (const tommath_int& o) { if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); if(o.m_data.dp) detail::check_tommath_result(mp_copy(const_cast< ::mp_int*>(&o.m_data), &m_data)); return *this; } tommath_int& operator = (boost::ulong_long_type i) { if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); boost::ulong_long_type mask = ((1uLL << std::numeric_limits::digits) - 1); unsigned shift = 0; ::mp_int t; detail::check_tommath_result(mp_init(&t)); mp_zero(&m_data); while(i) { detail::check_tommath_result(mp_set_int(&t, static_cast(i & mask))); if(shift) detail::check_tommath_result(mp_mul_2d(&t, shift, &t)); detail::check_tommath_result((mp_add(&m_data, &t, &m_data))); shift += std::numeric_limits::digits; i >>= std::numeric_limits::digits; } mp_clear(&t); return *this; } tommath_int& operator = (boost::long_long_type i) { if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); bool neg = i < 0; *this = boost::multiprecision::detail::unsigned_abs(i); if(neg) detail::check_tommath_result(mp_neg(&m_data, &m_data)); return *this; } // // Note that although mp_set_int takes an unsigned long as an argument // it only sets the first 32-bits to the result, and ignores the rest. // So use uint32_t as the largest type to pass to this function. // tommath_int& operator = (boost::uint32_t i) { if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); detail::check_tommath_result((mp_set_int(&m_data, i))); return *this; } tommath_int& operator = (boost::int32_t i) { if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); bool neg = i < 0; *this = boost::multiprecision::detail::unsigned_abs(i); if(neg) detail::check_tommath_result(mp_neg(&m_data, &m_data)); return *this; } tommath_int& operator = (long double a) { using std::frexp; using std::ldexp; using std::floor; if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); if (a == 0) { detail::check_tommath_result(mp_set_int(&m_data, 0)); return *this; } if (a == 1) { detail::check_tommath_result(mp_set_int(&m_data, 1)); return *this; } BOOST_ASSERT(!(boost::math::isinf)(a)); BOOST_ASSERT(!(boost::math::isnan)(a)); int e; long double f, term; detail::check_tommath_result(mp_set_int(&m_data, 0u)); ::mp_int t; detail::check_tommath_result(mp_init(&t)); f = frexp(a, &e); static const int shift = std::numeric_limits::digits - 1; while(f) { // extract int sized bits from f: f = ldexp(f, shift); term = floor(f); e -= shift; detail::check_tommath_result(mp_mul_2d(&m_data, shift, &m_data)); if(term > 0) { detail::check_tommath_result(mp_set_int(&t, static_cast(term))); detail::check_tommath_result(mp_add(&m_data, &t, &m_data)); } else { detail::check_tommath_result(mp_set_int(&t, static_cast(-term))); detail::check_tommath_result(mp_sub(&m_data, &t, &m_data)); } f -= term; } if(e > 0) detail::check_tommath_result(mp_mul_2d(&m_data, e, &m_data)); else if(e < 0) { tommath_int t2; detail::check_tommath_result(mp_div_2d(&m_data, -e, &m_data, &t2.data())); } mp_clear(&t); return *this; } tommath_int& operator = (const char* s) { // // We don't use libtommath's own routine because it doesn't error check the input :-( // if(m_data.dp == 0) detail::check_tommath_result(mp_init(&m_data)); std::size_t n = s ? std::strlen(s) : 0; *this = static_cast(0u); unsigned radix = 10; bool isneg = false; if(n && (*s == '-')) { --n; ++s; isneg = true; } if(n && (*s == '0')) { if((n > 1) && ((s[1] == 'x') || (s[1] == 'X'))) { radix = 16; s +=2; n -= 2; } else { radix = 8; n -= 1; } } if(n) { if(radix == 8 || radix == 16) { unsigned shift = radix == 8 ? 3 : 4; unsigned block_count = DIGIT_BIT / shift; unsigned block_shift = shift * block_count; boost::ulong_long_type val, block; while(*s) { block = 0; for(unsigned i = 0; (i < block_count); ++i) { if(*s >= '0' && *s <= '9') val = *s - '0'; else if(*s >= 'a' && *s <= 'f') val = 10 + *s - 'a'; else if(*s >= 'A' && *s <= 'F') val = 10 + *s - 'A'; else val = 400; if(val > radix) { BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected content found while parsing character string.")); } block <<= shift; block |= val; if(!*++s) { // final shift is different: block_shift = (i + 1) * shift; break; } } detail::check_tommath_result(mp_mul_2d(&data(), block_shift, &data())); if(data().used) data().dp[0] |= block; else *this = block; } } else { // Base 10, we extract blocks of size 10^9 at a time, that way // the number of multiplications is kept to a minimum: boost::uint32_t block_mult = 1000000000; while(*s) { boost::uint32_t block = 0; for(unsigned i = 0; i < 9; ++i) { boost::uint32_t val; if(*s >= '0' && *s <= '9') val = *s - '0'; else BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected character encountered in input.")); block *= 10; block += val; if(!*++s) { static const boost::uint32_t block_multiplier[9] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; block_mult = block_multiplier[i]; break; } } tommath_int t; t = block_mult; eval_multiply(*this, t); t = block; eval_add(*this, t); } } } if(isneg) this->negate(); return *this; } std::string str(std::streamsize /*digits*/, std::ios_base::fmtflags f)const { BOOST_ASSERT(m_data.dp); int base = 10; if((f & std::ios_base::oct) == std::ios_base::oct) base = 8; else if((f & std::ios_base::hex) == std::ios_base::hex) base = 16; // // sanity check, bases 8 and 16 are only available for positive numbers: // if((base != 10) && m_data.sign) BOOST_THROW_EXCEPTION(std::runtime_error("Formatted output in bases 8 or 16 is only available for positive numbers")); int s; detail::check_tommath_result(mp_radix_size(const_cast< ::mp_int*>(&m_data), base, &s)); boost::scoped_array a(new char[s+1]); detail::check_tommath_result(mp_toradix_n(const_cast< ::mp_int*>(&m_data), a.get(), base, s+1)); std::string result = a.get(); if((base != 10) && (f & std::ios_base::showbase)) { int pos = result[0] == '-' ? 1 : 0; const char* pp = base == 8 ? "0" : "0x"; result.insert(static_cast(pos), pp); } if((f & std::ios_base::showpos) && (result[0] != '-')) result.insert(static_cast(0), 1, '+'); return result; } ~tommath_int() { if(m_data.dp) mp_clear(&m_data); } void negate() { BOOST_ASSERT(m_data.dp); mp_neg(&m_data, &m_data); } int compare(const tommath_int& o)const { BOOST_ASSERT(m_data.dp && o.m_data.dp); return mp_cmp(const_cast< ::mp_int*>(&m_data), const_cast< ::mp_int*>(&o.m_data)); } template int compare(V v)const { tommath_int d; tommath_int t(*this); detail::check_tommath_result(mp_shrink(&t.data())); d = v; return t.compare(d); } ::mp_int& data() { BOOST_ASSERT(m_data.dp); return m_data; } const ::mp_int& data()const { BOOST_ASSERT(m_data.dp); return m_data; } void swap(tommath_int& o)BOOST_NOEXCEPT { mp_exch(&m_data, &o.data()); } protected: ::mp_int m_data; }; #define BOOST_MP_TOMMATH_BIT_OP_CHECK(x)\ if(SIGN(&x.data()))\ BOOST_THROW_EXCEPTION(std::runtime_error("Bitwise operations on libtommath negative valued integers are disabled as they produce unpredictable results")) int eval_get_sign(const tommath_int& val); inline void eval_add(tommath_int& t, const tommath_int& o) { detail::check_tommath_result(mp_add(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_subtract(tommath_int& t, const tommath_int& o) { detail::check_tommath_result(mp_sub(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_multiply(tommath_int& t, const tommath_int& o) { detail::check_tommath_result(mp_mul(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_divide(tommath_int& t, const tommath_int& o) { using default_ops::eval_is_zero; tommath_int temp; if(eval_is_zero(o)) BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); detail::check_tommath_result(mp_div(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data(), &temp.data())); } inline void eval_modulus(tommath_int& t, const tommath_int& o) { using default_ops::eval_is_zero; if(eval_is_zero(o)) BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); bool neg = eval_get_sign(t) < 0; bool neg2 = eval_get_sign(o) < 0; detail::check_tommath_result(mp_mod(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); if((neg != neg2) && (eval_get_sign(t) != 0)) { t.negate(); detail::check_tommath_result(mp_add(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); t.negate(); } else if(neg && (t.compare(o) == 0)) { mp_zero(&t.data()); } } template inline void eval_left_shift(tommath_int& t, UI i) { detail::check_tommath_result(mp_mul_2d(&t.data(), static_cast(i), &t.data())); } template inline void eval_right_shift(tommath_int& t, UI i) { tommath_int d; detail::check_tommath_result(mp_div_2d(&t.data(), static_cast(i), &t.data(), &d.data())); } template inline void eval_left_shift(tommath_int& t, const tommath_int& v, UI i) { detail::check_tommath_result(mp_mul_2d(const_cast< ::mp_int*>(&v.data()), static_cast(i), &t.data())); } template inline void eval_right_shift(tommath_int& t, const tommath_int& v, UI i) { tommath_int d; detail::check_tommath_result(mp_div_2d(const_cast< ::mp_int*>(&v.data()), static_cast(i), &t.data(), &d.data())); } inline void eval_bitwise_and(tommath_int& result, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(result); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_and(&result.data(), const_cast< ::mp_int*>(&v.data()), &result.data())); } inline void eval_bitwise_or(tommath_int& result, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(result); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_or(&result.data(), const_cast< ::mp_int*>(&v.data()), &result.data())); } inline void eval_bitwise_xor(tommath_int& result, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(result); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_xor(&result.data(), const_cast< ::mp_int*>(&v.data()), &result.data())); } inline void eval_add(tommath_int& t, const tommath_int& p, const tommath_int& o) { detail::check_tommath_result(mp_add(const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_subtract(tommath_int& t, const tommath_int& p, const tommath_int& o) { detail::check_tommath_result(mp_sub(const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_multiply(tommath_int& t, const tommath_int& p, const tommath_int& o) { detail::check_tommath_result(mp_mul(const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&o.data()), &t.data())); } inline void eval_divide(tommath_int& t, const tommath_int& p, const tommath_int& o) { using default_ops::eval_is_zero; tommath_int d; if(eval_is_zero(o)) BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); detail::check_tommath_result(mp_div(const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&o.data()), &t.data(), &d.data())); } inline void eval_modulus(tommath_int& t, const tommath_int& p, const tommath_int& o) { using default_ops::eval_is_zero; if(eval_is_zero(o)) BOOST_THROW_EXCEPTION(std::overflow_error("Integer division by zero")); bool neg = eval_get_sign(p) < 0; bool neg2 = eval_get_sign(o) < 0; detail::check_tommath_result(mp_mod(const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&o.data()), &t.data())); if((neg != neg2) && (eval_get_sign(t) != 0)) { t.negate(); detail::check_tommath_result(mp_add(&t.data(), const_cast< ::mp_int*>(&o.data()), &t.data())); t.negate(); } else if(neg && (t.compare(o) == 0)) { mp_zero(&t.data()); } } inline void eval_bitwise_and(tommath_int& result, const tommath_int& u, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(u); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_and(const_cast< ::mp_int*>(&u.data()), const_cast< ::mp_int*>(&v.data()), &result.data())); } inline void eval_bitwise_or(tommath_int& result, const tommath_int& u, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(u); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_or(const_cast< ::mp_int*>(&u.data()), const_cast< ::mp_int*>(&v.data()), &result.data())); } inline void eval_bitwise_xor(tommath_int& result, const tommath_int& u, const tommath_int& v) { BOOST_MP_TOMMATH_BIT_OP_CHECK(u); BOOST_MP_TOMMATH_BIT_OP_CHECK(v); detail::check_tommath_result(mp_xor(const_cast< ::mp_int*>(&u.data()), const_cast< ::mp_int*>(&v.data()), &result.data())); } /* inline void eval_complement(tommath_int& result, const tommath_int& u) { // // Although this code works, it doesn't really do what the user might expect.... // and it's hard to see how it ever could. Disabled for now: // result = u; for(int i = 0; i < result.data().used; ++i) { result.data().dp[i] = MP_MASK & ~(result.data().dp[i]); } // // We now need to pad out the left of the value with 1's to round up to a whole number of // CHAR_BIT * sizeof(mp_digit) units. Otherwise we'll end up with a very strange number of // bits set! // unsigned shift = result.data().used * DIGIT_BIT; // How many bits we're actually using // How many bits we actually need, reduced by one to account for a mythical sign bit: int padding = result.data().used * std::numeric_limits::digits - shift - 1; while(padding >= std::numeric_limits::digits) padding -= std::numeric_limits::digits; // Create a mask providing the extra bits we need and add to result: tommath_int mask; mask = static_cast((1u << padding) - 1); eval_left_shift(mask, shift); add(result, mask); } */ inline bool eval_is_zero(const tommath_int& val) { return mp_iszero(&val.data()); } inline int eval_get_sign(const tommath_int& val) { return mp_iszero(&val.data()) ? 0 : SIGN(&val.data()) ? -1 : 1; } template inline void eval_convert_to(A* result, const tommath_int& val) { *result = boost::lexical_cast(val.str(0, std::ios_base::fmtflags(0))); } inline void eval_convert_to(char* result, const tommath_int& val) { *result = static_cast(boost::lexical_cast(val.str(0, std::ios_base::fmtflags(0)))); } inline void eval_convert_to(unsigned char* result, const tommath_int& val) { *result = static_cast(boost::lexical_cast(val.str(0, std::ios_base::fmtflags(0)))); } inline void eval_convert_to(signed char* result, const tommath_int& val) { *result = static_cast(boost::lexical_cast(val.str(0, std::ios_base::fmtflags(0)))); } inline void eval_abs(tommath_int& result, const tommath_int& val) { detail::check_tommath_result(mp_abs(const_cast< ::mp_int*>(&val.data()), &result.data())); } inline void eval_gcd(tommath_int& result, const tommath_int& a, const tommath_int& b) { detail::check_tommath_result(mp_gcd(const_cast< ::mp_int*>(&a.data()), const_cast< ::mp_int*>(&b.data()), const_cast< ::mp_int*>(&result.data()))); } inline void eval_lcm(tommath_int& result, const tommath_int& a, const tommath_int& b) { detail::check_tommath_result(mp_lcm(const_cast< ::mp_int*>(&a.data()), const_cast< ::mp_int*>(&b.data()), const_cast< ::mp_int*>(&result.data()))); } inline void eval_powm(tommath_int& result, const tommath_int& base, const tommath_int& p, const tommath_int& m) { if(eval_get_sign(p) < 0) { BOOST_THROW_EXCEPTION(std::runtime_error("powm requires a positive exponent.")); } detail::check_tommath_result(mp_exptmod(const_cast< ::mp_int*>(&base.data()), const_cast< ::mp_int*>(&p.data()), const_cast< ::mp_int*>(&m.data()), &result.data())); } inline void eval_qr(const tommath_int& x, const tommath_int& y, tommath_int& q, tommath_int& r) { detail::check_tommath_result(mp_div(const_cast< ::mp_int*>(&x.data()), const_cast< ::mp_int*>(&y.data()), &q.data(), &r.data())); } inline unsigned eval_lsb(const tommath_int& val) { int c = eval_get_sign(val); if(c == 0) { BOOST_THROW_EXCEPTION(std::range_error("No bits were set in the operand.")); } if(c < 0) { BOOST_THROW_EXCEPTION(std::range_error("Testing individual bits in negative values is not supported - results are undefined.")); } return mp_cnt_lsb(const_cast< ::mp_int*>(&val.data())); } inline unsigned eval_msb(const tommath_int& val) { int c = eval_get_sign(val); if(c == 0) { BOOST_THROW_EXCEPTION(std::range_error("No bits were set in the operand.")); } if(c < 0) { BOOST_THROW_EXCEPTION(std::range_error("Testing individual bits in negative values is not supported - results are undefined.")); } return mp_count_bits(const_cast< ::mp_int*>(&val.data())) - 1; } template inline typename enable_if, Integer>::type eval_integer_modulus(const tommath_int& x, Integer val) { static const mp_digit m = (static_cast(1) << DIGIT_BIT) - 1; if(val <= m) { mp_digit d; detail::check_tommath_result(mp_mod_d(const_cast< ::mp_int*>(&x.data()), static_cast(val), &d)); return d; } else { return default_ops::eval_integer_modulus(x, val); } } template inline typename enable_if, Integer>::type eval_integer_modulus(const tommath_int& x, Integer val) { return eval_integer_modulus(x, boost::multiprecision::detail::unsigned_abs(val)); } } // namespace backends using boost::multiprecision::backends::tommath_int; template<> struct number_category : public mpl::int_{}; typedef number tom_int; typedef rational_adaptor tommath_rational; typedef number tom_rational; }} // namespaces namespace std{ template class numeric_limits > { typedef boost::multiprecision::number number_type; public: BOOST_STATIC_CONSTEXPR bool is_specialized = true; // // Largest and smallest numbers are bounded only by available memory, set // to zero: // static number_type (min)() { return number_type(); } static number_type (max)() { return number_type(); } static number_type lowest() { return (min)(); } BOOST_STATIC_CONSTEXPR int digits = INT_MAX; BOOST_STATIC_CONSTEXPR int digits10 = (INT_MAX / 1000) * 301L; BOOST_STATIC_CONSTEXPR int max_digits10 = digits10 + 2; BOOST_STATIC_CONSTEXPR bool is_signed = true; BOOST_STATIC_CONSTEXPR bool is_integer = true; BOOST_STATIC_CONSTEXPR bool is_exact = true; BOOST_STATIC_CONSTEXPR int radix = 2; static number_type epsilon() { return number_type(); } static number_type round_error() { return number_type(); } BOOST_STATIC_CONSTEXPR int min_exponent = 0; BOOST_STATIC_CONSTEXPR int min_exponent10 = 0; BOOST_STATIC_CONSTEXPR int max_exponent = 0; BOOST_STATIC_CONSTEXPR int max_exponent10 = 0; BOOST_STATIC_CONSTEXPR bool has_infinity = false; BOOST_STATIC_CONSTEXPR bool has_quiet_NaN = false; BOOST_STATIC_CONSTEXPR bool has_signaling_NaN = false; BOOST_STATIC_CONSTEXPR float_denorm_style has_denorm = denorm_absent; BOOST_STATIC_CONSTEXPR bool has_denorm_loss = false; static number_type infinity() { return number_type(); } static number_type quiet_NaN() { return number_type(); } static number_type signaling_NaN() { return number_type(); } static number_type denorm_min() { return number_type(); } BOOST_STATIC_CONSTEXPR bool is_iec559 = false; BOOST_STATIC_CONSTEXPR bool is_bounded = false; BOOST_STATIC_CONSTEXPR bool is_modulo = false; BOOST_STATIC_CONSTEXPR bool traps = false; BOOST_STATIC_CONSTEXPR bool tinyness_before = false; BOOST_STATIC_CONSTEXPR float_round_style round_style = round_toward_zero; }; #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::digits; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::digits10; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::max_digits10; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_signed; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_integer; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_exact; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::radix; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::min_exponent; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::min_exponent10; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::max_exponent; template BOOST_CONSTEXPR_OR_CONST int numeric_limits >::max_exponent10; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::has_infinity; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::has_quiet_NaN; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::has_signaling_NaN; template BOOST_CONSTEXPR_OR_CONST float_denorm_style numeric_limits >::has_denorm; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::has_denorm_loss; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_iec559; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_bounded; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::is_modulo; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::traps; template BOOST_CONSTEXPR_OR_CONST bool numeric_limits >::tinyness_before; template BOOST_CONSTEXPR_OR_CONST float_round_style numeric_limits >::round_style; #endif } #endif