diff --git a/include/boost/multiprecision/detail/generic_interconvert.hpp b/include/boost/multiprecision/detail/generic_interconvert.hpp index acead0c8..ce39f151 100644 --- a/include/boost/multiprecision/detail/generic_interconvert.hpp +++ b/include/boost/multiprecision/detail/generic_interconvert.hpp @@ -210,24 +210,127 @@ void generic_interconvert(To& to, const From& from, const mpl::int_ -void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) +template +inline typename disable_if_c::value || is_floating_point::value>::type + generic_convert_rational_to_float_imp(To& result, const Integer& n, const Integer& d, const mpl::true_&) { - typedef typename component_type >::type from_component_type; + // + // If we get here, then there's something about one type or the other + // that prevents an exactly rounded result from being calculated + // (or at least it's not clear how to implement such a thing). + // using default_ops::eval_divide; - - number t(from); - from_component_type n(numerator(t)), d(denominator(t)); number fn(n), fd(d); - eval_divide(to, fn.backend(), fd.backend()); + eval_divide(result, fn.backend(), fd.backend()); +} +template +inline typename enable_if_c::value || is_floating_point::value>::type + generic_convert_rational_to_float_imp(To& result, const Integer& n, const Integer& d, const mpl::true_&) +{ + // + // If we get here, then there's something about one type or the other + // that prevents an exactly rounded result from being calculated + // (or at least it's not clear how to implement such a thing). + // + To fd(d); + result = n; + result /= fd; } -namespace detail{ +template +typename enable_if_c::value || is_floating_point::value>::type + generic_convert_rational_to_float_imp(To& result, Integer& num, Integer& denom, const mpl::false_&) +{ + // + // If we get here, then the precision of type To is known, and the integer type is unbounded + // so we can use integer division plus manipulation of the remainder to get an exactly + // rounded result. + // + bool s = false; + if(num < 0) + { + s = true; + num = -num; + } + int denom_bits = msb(denom); + int shift = std::numeric_limits::digits + denom_bits - msb(num); + if(shift > 0) + num <<= shift; + else if(shift < 0) + denom <<= shift; + Integer q, r; + divide_qr(num, denom, q, r); + int q_bits = msb(q); + if(q_bits == std::numeric_limits::digits) + { + // + // Round up if 2 * r > denom: + // + r <<= 1; + int c = r.compare(denom); + if(c > 0) + ++q; + else if((c == 0) && (q & 1u)) + { + ++q; + } + } + else + { + BOOST_ASSERT(q_bits == 1 + std::numeric_limits::digits); + // + // We basically already have the rounding info: + // + if((q & 1u) && r) + ++q; + } + using std::ldexp; + result = static_cast(q); + result = ldexp(result, -shift); + if(s) + result = -result; +} +template +inline typename disable_if_c::value || is_floating_point::value>::type + generic_convert_rational_to_float_imp(To& result, Integer& num, Integer& denom, const mpl::false_& tag) +{ + number t; + generic_convert_rational_to_float_imp(t, num, denom, tag); + result = t.backend(); +} + +template +inline void generic_convert_rational_to_float(To& result, const From& f) +{ + // + // Type From is always a Backend to number<>, or an + // instance of number<>, but we allow + // To to be either a Backend type, or a real number type, + // that way we can call this from generic conversions, and + // from specific conversions to built in types. + // + typedef typename mpl::if_c::value, From, number >::type actual_from_type; + typedef typename mpl::if_c::value || is_floating_point::value, To, number >::type actual_to_type; + typedef typename component_type::type integer_type; + typedef mpl::bool_::is_specialized + || std::numeric_limits::is_bounded + || !std::numeric_limits::is_specialized + || !std::numeric_limits::is_bounded + || (std::numeric_limits::radix != 2)> dispatch_tag; + + integer_type n(numerator(static_cast(f))), d(denominator(static_cast(f))); + generic_convert_rational_to_float_imp(result, n, d, dispatch_tag()); +} + +template +inline void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) +{ + generic_convert_rational_to_float(to, from); +} template void generic_interconvert_float2rational(To& to, const From& from, const mpl::int_<2>& /*radix*/) { - BOOST_MATH_STD_USING typedef typename mpl::front::type ui_type; static const int shift = std::numeric_limits::digits; typename From::exponent_type e; @@ -285,12 +388,10 @@ void generic_interconvert_float2rational(To& to, const From& from, const mpl::in assign_components(to, num, denom); } -} - template void generic_interconvert(To& to, const From& from, const mpl::int_& /*to_type*/, const mpl::int_& /*from_type*/) { - detail::generic_interconvert_float2rational(to, from, mpl::int_ >::radix>()); + generic_interconvert_float2rational(to, from, mpl::int_ >::radix>()); } }}} // namespaces diff --git a/include/boost/multiprecision/rational_adaptor.hpp b/include/boost/multiprecision/rational_adaptor.hpp index 7db31a18..9285bae9 100644 --- a/include/boost/multiprecision/rational_adaptor.hpp +++ b/include/boost/multiprecision/rational_adaptor.hpp @@ -271,11 +271,17 @@ R safe_convert_to_float(const LargeInteger& i) template inline typename enable_if_c::value == number_kind_floating_point>::type eval_convert_to(R* result, const rational_adaptor& backend) { + // + // The generic conversion is as good as anything we can write here: + // + ::boost::multiprecision::detail::generic_convert_rational_to_float(*result, backend); + /* typedef typename component_type > >::type comp_t; comp_t num(backend.data().numerator()); comp_t denom(backend.data().denominator()); *result = safe_convert_to_float(num); *result /= safe_convert_to_float(denom); + */ } template