Chris@16: /////////////////////////////////////////////////////////////////////////////// Chris@16: // Copyright 2011 John Maddock. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_MP_GENERIC_INTERCONVERT_HPP Chris@16: #define BOOST_MP_GENERIC_INTERCONVERT_HPP Chris@16: Chris@16: #include Chris@16: Chris@101: #ifdef BOOST_MSVC Chris@101: #pragma warning(push) Chris@101: #pragma warning(disable:4127) Chris@101: #endif Chris@101: Chris@16: namespace boost{ namespace multiprecision{ namespace detail{ Chris@16: Chris@16: template Chris@101: inline To do_cast(const From & from) Chris@101: { Chris@101: return static_cast(from); Chris@101: } Chris@101: template Chris@101: inline To do_cast(const number& from) Chris@101: { Chris@101: return from.template convert_to(); Chris@101: } Chris@101: Chris@101: template Chris@16: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@16: { Chris@16: using default_ops::eval_get_sign; Chris@16: using default_ops::eval_bitwise_and; Chris@16: using default_ops::eval_convert_to; Chris@16: using default_ops::eval_right_shift; Chris@16: using default_ops::eval_ldexp; Chris@16: using default_ops::eval_add; Chris@16: // smallest unsigned type handled natively by "From" is likely to be it's limb_type: Chris@16: typedef typename canonical::type limb_type; Chris@16: // get the corresponding type that we can assign to "To": Chris@16: typedef typename canonical::type to_type; Chris@16: From t(from); Chris@16: bool is_neg = eval_get_sign(t) < 0; Chris@16: if(is_neg) Chris@16: t.negate(); Chris@16: // Pick off the first limb: Chris@16: limb_type limb; Chris@16: limb_type mask = ~static_cast(0); Chris@16: From fl; Chris@16: eval_bitwise_and(fl, t, mask); Chris@16: eval_convert_to(&limb, fl); Chris@16: to = static_cast(limb); Chris@16: eval_right_shift(t, std::numeric_limits::digits); Chris@16: // Chris@16: // Then keep picking off more limbs until "t" is zero: Chris@16: // Chris@16: To l; Chris@16: unsigned shift = std::numeric_limits::digits; Chris@16: while(!eval_is_zero(t)) Chris@16: { Chris@16: eval_bitwise_and(fl, t, mask); Chris@16: eval_convert_to(&limb, fl); Chris@16: l = static_cast(limb); Chris@16: eval_right_shift(t, std::numeric_limits::digits); Chris@16: eval_ldexp(l, l, shift); Chris@16: eval_add(to, l); Chris@16: shift += std::numeric_limits::digits; Chris@16: } Chris@16: // Chris@16: // Finish off by setting the sign: Chris@16: // Chris@16: if(is_neg) Chris@16: to.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@16: { Chris@16: using default_ops::eval_get_sign; Chris@16: using default_ops::eval_bitwise_and; Chris@16: using default_ops::eval_convert_to; Chris@16: using default_ops::eval_right_shift; Chris@16: using default_ops::eval_left_shift; Chris@16: using default_ops::eval_bitwise_or; Chris@16: using default_ops::eval_is_zero; Chris@16: // smallest unsigned type handled natively by "From" is likely to be it's limb_type: Chris@16: typedef typename canonical::type limb_type; Chris@16: // get the corresponding type that we can assign to "To": Chris@16: typedef typename canonical::type to_type; Chris@16: From t(from); Chris@16: bool is_neg = eval_get_sign(t) < 0; Chris@16: if(is_neg) Chris@16: t.negate(); Chris@16: // Pick off the first limb: Chris@16: limb_type limb; Chris@16: limb_type mask = static_cast(~static_cast(0)); Chris@16: From fl; Chris@16: eval_bitwise_and(fl, t, mask); Chris@16: eval_convert_to(&limb, fl); Chris@16: to = static_cast(limb); Chris@16: eval_right_shift(t, std::numeric_limits::digits); Chris@16: // Chris@16: // Then keep picking off more limbs until "t" is zero: Chris@16: // Chris@16: To l; Chris@16: unsigned shift = std::numeric_limits::digits; Chris@16: while(!eval_is_zero(t)) Chris@16: { Chris@16: eval_bitwise_and(fl, t, mask); Chris@16: eval_convert_to(&limb, fl); Chris@16: l = static_cast(limb); Chris@16: eval_right_shift(t, std::numeric_limits::digits); Chris@16: eval_left_shift(l, shift); Chris@16: eval_bitwise_or(to, l); Chris@16: shift += std::numeric_limits::digits; Chris@16: } Chris@16: // Chris@16: // Finish off by setting the sign: Chris@16: // Chris@16: if(is_neg) Chris@16: to.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@16: { Chris@16: #ifdef BOOST_MSVC Chris@16: #pragma warning(push) Chris@16: #pragma warning(disable:4127) Chris@16: #endif Chris@16: // Chris@16: // The code here only works when the radix of "From" is 2, we could try shifting by other Chris@16: // radixes but it would complicate things.... use a string conversion when the radix is other Chris@16: // than 2: Chris@16: // Chris@16: if(std::numeric_limits >::radix != 2) Chris@16: { Chris@16: to = from.str(0, std::ios_base::fmtflags()).c_str(); Chris@16: return; Chris@16: } Chris@16: Chris@16: Chris@16: typedef typename canonical::type ui_type; Chris@16: Chris@16: using default_ops::eval_fpclassify; Chris@16: using default_ops::eval_add; Chris@16: using default_ops::eval_subtract; Chris@16: using default_ops::eval_convert_to; Chris@16: Chris@16: // Chris@16: // First classify the input, then handle the special cases: Chris@16: // Chris@16: int c = eval_fpclassify(from); Chris@16: Chris@101: if(c == (int)FP_ZERO) Chris@16: { Chris@16: to = ui_type(0); Chris@16: return; Chris@16: } Chris@101: else if(c == (int)FP_NAN) Chris@16: { Chris@101: to = static_cast("nan"); Chris@16: return; Chris@16: } Chris@101: else if(c == (int)FP_INFINITE) Chris@16: { Chris@101: to = static_cast("inf"); Chris@16: if(eval_get_sign(from) < 0) Chris@16: to.negate(); Chris@16: return; Chris@16: } Chris@16: Chris@16: typename From::exponent_type e; Chris@16: From f, term; Chris@16: to = ui_type(0); Chris@16: Chris@16: eval_frexp(f, from, &e); Chris@16: Chris@16: static const int shift = std::numeric_limits::digits - 1; Chris@16: Chris@16: while(!eval_is_zero(f)) Chris@16: { Chris@16: // extract int sized bits from f: Chris@16: eval_ldexp(f, f, shift); Chris@16: eval_floor(term, f); Chris@16: e -= shift; Chris@16: eval_ldexp(to, to, shift); Chris@16: typename boost::multiprecision::detail::canonical::type ll; Chris@16: eval_convert_to(&ll, term); Chris@16: eval_add(to, ll); Chris@16: eval_subtract(f, term); Chris@16: } Chris@16: typedef typename To::exponent_type to_exponent; Chris@16: if((e > (std::numeric_limits::max)()) || (e < (std::numeric_limits::min)())) Chris@16: { Chris@101: to = static_cast("inf"); Chris@16: if(eval_get_sign(from) < 0) Chris@16: to.negate(); Chris@16: return; Chris@16: } Chris@16: eval_ldexp(to, to, static_cast(e)); Chris@16: #ifdef BOOST_MSVC Chris@16: #pragma warning(pop) Chris@16: #endif Chris@16: } Chris@16: Chris@16: template Chris@16: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@16: { Chris@16: typedef typename component_type >::type to_component_type; Chris@16: Chris@16: number t(from); Chris@16: to_component_type n(numerator(t)), d(denominator(t)); Chris@16: using default_ops::assign_components; Chris@16: assign_components(to, n.backend(), d.backend()); Chris@16: } Chris@16: Chris@16: template Chris@16: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@16: { Chris@16: typedef typename component_type >::type to_component_type; Chris@16: Chris@16: number t(from); Chris@16: to_component_type n(t), d(1); Chris@16: using default_ops::assign_components; Chris@16: assign_components(to, n.backend(), d.backend()); Chris@16: } Chris@16: Chris@101: template Chris@101: R safe_convert_to_float(const LargeInteger& i) Chris@101: { Chris@101: using std::ldexp; Chris@101: if(!i) Chris@101: return R(0); Chris@101: if(std::numeric_limits::is_specialized && std::numeric_limits::max_exponent) Chris@101: { Chris@101: LargeInteger val(i); Chris@101: if(val.sign() < 0) Chris@101: val = -val; Chris@101: unsigned mb = msb(val); Chris@101: if(mb >= std::numeric_limits::max_exponent) Chris@101: { Chris@101: int scale_factor = (int)mb + 1 - std::numeric_limits::max_exponent; Chris@101: BOOST_ASSERT(scale_factor >= 1); Chris@101: val >>= scale_factor; Chris@101: R result = val.template convert_to(); Chris@101: if(std::numeric_limits::digits == 0 || std::numeric_limits::digits >= std::numeric_limits::max_exponent) Chris@101: { Chris@101: // Chris@101: // Calculate and add on the remainder, only if there are more Chris@101: // digits in the mantissa that the size of the exponent, in Chris@101: // other words if we are dropping digits in the conversion Chris@101: // otherwise: Chris@101: // Chris@101: LargeInteger remainder(i); Chris@101: remainder &= (LargeInteger(1) << scale_factor) - 1; Chris@101: result += ldexp(safe_convert_to_float(remainder), -scale_factor); Chris@101: } Chris@101: return i.sign() < 0 ? static_cast(-result) : result; Chris@101: } Chris@101: } Chris@101: return i.template convert_to(); Chris@101: } Chris@101: Chris@101: template Chris@101: inline typename disable_if_c::value || is_floating_point::value>::type Chris@101: generic_convert_rational_to_float_imp(To& result, const Integer& n, const Integer& d, const mpl::true_&) Chris@101: { Chris@101: // Chris@101: // If we get here, then there's something about one type or the other Chris@101: // that prevents an exactly rounded result from being calculated Chris@101: // (or at least it's not clear how to implement such a thing). Chris@101: // Chris@101: using default_ops::eval_divide; Chris@101: number fn(safe_convert_to_float >(n)), fd(safe_convert_to_float >(d)); Chris@101: eval_divide(result, fn.backend(), fd.backend()); Chris@101: } Chris@101: template Chris@101: inline typename enable_if_c::value || is_floating_point::value>::type Chris@101: generic_convert_rational_to_float_imp(To& result, const Integer& n, const Integer& d, const mpl::true_&) Chris@101: { Chris@101: // Chris@101: // If we get here, then there's something about one type or the other Chris@101: // that prevents an exactly rounded result from being calculated Chris@101: // (or at least it's not clear how to implement such a thing). Chris@101: // Chris@101: To fd(safe_convert_to_float(d)); Chris@101: result = safe_convert_to_float(n); Chris@101: result /= fd; Chris@101: } Chris@101: Chris@101: template Chris@101: typename enable_if_c::value || is_floating_point::value>::type Chris@101: generic_convert_rational_to_float_imp(To& result, Integer& num, Integer& denom, const mpl::false_&) Chris@101: { Chris@101: // Chris@101: // If we get here, then the precision of type To is known, and the integer type is unbounded Chris@101: // so we can use integer division plus manipulation of the remainder to get an exactly Chris@101: // rounded result. Chris@101: // Chris@101: if(num == 0) Chris@101: { Chris@101: result = 0; Chris@101: return; Chris@101: } Chris@101: bool s = false; Chris@101: if(num < 0) Chris@101: { Chris@101: s = true; Chris@101: num = -num; Chris@101: } Chris@101: int denom_bits = msb(denom); Chris@101: int shift = std::numeric_limits::digits + denom_bits - msb(num) + 1; Chris@101: if(shift > 0) Chris@101: num <<= shift; Chris@101: else if(shift < 0) Chris@101: denom <<= std::abs(shift); Chris@101: Integer q, r; Chris@101: divide_qr(num, denom, q, r); Chris@101: int q_bits = msb(q); Chris@101: if(q_bits == std::numeric_limits::digits) Chris@101: { Chris@101: // Chris@101: // Round up if 2 * r > denom: Chris@101: // Chris@101: r <<= 1; Chris@101: int c = r.compare(denom); Chris@101: if(c > 0) Chris@101: ++q; Chris@101: else if((c == 0) && (q & 1u)) Chris@101: { Chris@101: ++q; Chris@101: } Chris@101: } Chris@101: else Chris@101: { Chris@101: BOOST_ASSERT(q_bits == 1 + std::numeric_limits::digits); Chris@101: // Chris@101: // We basically already have the rounding info: Chris@101: // Chris@101: if(q & 1u) Chris@101: { Chris@101: if(r || (q & 2u)) Chris@101: ++q; Chris@101: } Chris@101: } Chris@101: using std::ldexp; Chris@101: result = do_cast(q); Chris@101: result = ldexp(result, -shift); Chris@101: if(s) Chris@101: result = -result; Chris@101: } Chris@101: template Chris@101: inline typename disable_if_c::value || is_floating_point::value>::type Chris@101: generic_convert_rational_to_float_imp(To& result, Integer& num, Integer& denom, const mpl::false_& tag) Chris@101: { Chris@101: number t; Chris@101: generic_convert_rational_to_float_imp(t, num, denom, tag); Chris@101: result = t.backend(); Chris@101: } Chris@101: Chris@16: template Chris@101: inline void generic_convert_rational_to_float(To& result, const From& f) Chris@16: { Chris@101: // Chris@101: // Type From is always a Backend to number<>, or an Chris@101: // instance of number<>, but we allow Chris@101: // To to be either a Backend type, or a real number type, Chris@101: // that way we can call this from generic conversions, and Chris@101: // from specific conversions to built in types. Chris@101: // Chris@101: typedef typename mpl::if_c::value, From, number >::type actual_from_type; Chris@101: typedef typename mpl::if_c::value || is_floating_point::value, To, number >::type actual_to_type; Chris@101: typedef typename component_type::type integer_type; Chris@101: typedef mpl::bool_::is_specialized Chris@101: || std::numeric_limits::is_bounded Chris@101: || !std::numeric_limits::is_specialized Chris@101: || !std::numeric_limits::is_bounded Chris@101: || (std::numeric_limits::radix != 2)> dispatch_tag; Chris@16: Chris@101: integer_type n(numerator(static_cast(f))), d(denominator(static_cast(f))); Chris@101: generic_convert_rational_to_float_imp(result, n, d, dispatch_tag()); Chris@101: } Chris@101: Chris@101: template Chris@101: inline void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@101: { Chris@101: generic_convert_rational_to_float(to, from); Chris@101: } Chris@101: Chris@101: template Chris@101: void generic_interconvert_float2rational(To& to, const From& from, const mpl::int_<2>& /*radix*/) Chris@101: { Chris@101: typedef typename mpl::front::type ui_type; Chris@101: static const int shift = std::numeric_limits::digits; Chris@101: typename From::exponent_type e; Chris@101: typename component_type >::type num, denom; Chris@101: number val(from); Chris@101: val = frexp(val, &e); Chris@101: while(val) Chris@101: { Chris@101: val = ldexp(val, shift); Chris@101: e -= shift; Chris@101: long long ll = boost::math::lltrunc(val); Chris@101: val -= ll; Chris@101: num <<= shift; Chris@101: num += ll; Chris@101: } Chris@101: denom = ui_type(1u); Chris@101: if(e < 0) Chris@101: denom <<= -e; Chris@101: else if(e > 0) Chris@101: num <<= e; Chris@101: assign_components(to, num.backend(), denom.backend()); Chris@101: } Chris@101: Chris@101: template Chris@101: void generic_interconvert_float2rational(To& to, const From& from, const mpl::int_& /*radix*/) Chris@101: { Chris@101: // Chris@101: // This is almost the same as the binary case above, but we have to use Chris@101: // scalbn and ilogb rather than ldexp and frexp, we also only extract Chris@101: // one Radix digit at a time which is terribly inefficient! Chris@101: // Chris@101: typedef typename mpl::front::type ui_type; Chris@101: typename From::exponent_type e; Chris@101: typename component_type::type num, denom; Chris@101: number val(from); Chris@101: e = ilogb(val); Chris@101: val = scalbn(val, -e); Chris@101: while(val) Chris@101: { Chris@101: long long ll = boost::math::lltrunc(val); Chris@101: val -= ll; Chris@101: val = scalbn(val, 1); Chris@101: num *= Radix; Chris@101: num += ll; Chris@101: --e; Chris@101: } Chris@101: ++e; Chris@101: denom = ui_type(Radix); Chris@101: denom = pow(denom, abs(e)); Chris@101: if(e > 0) Chris@101: { Chris@101: num *= denom; Chris@101: denom = 1; Chris@101: } Chris@101: assign_components(to, num, denom); Chris@101: } Chris@101: Chris@101: template Chris@101: void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) Chris@101: { Chris@101: generic_interconvert_float2rational(to, from, mpl::int_ >::radix>()); Chris@16: } Chris@16: Chris@16: }}} // namespaces Chris@16: Chris@101: #ifdef BOOST_MSVC Chris@101: #pragma warning(pop) Chris@101: #endif Chris@101: Chris@16: #endif // BOOST_MP_GENERIC_INTERCONVERT_HPP Chris@16: