diff --git a/include/boost/multiprecision/cpp_bin_float.hpp b/include/boost/multiprecision/cpp_bin_float.hpp index 38f53875..3c6760ad 100644 --- a/include/boost/multiprecision/cpp_bin_float.hpp +++ b/include/boost/multiprecision/cpp_bin_float.hpp @@ -1237,26 +1237,25 @@ inline typename boost::enable_if_c::value>::type eval_con // typedef cpp_bin_float::digits, digit_base_2, void, Exponent, MinE, MaxE> conv_type; typedef typename common_type::type common_exp_type; - conv_type arg(original_arg); - switch(arg.exponent()) + switch(original_arg.exponent()) { - case conv_type::exponent_zero: + case cpp_bin_float::exponent_zero: *res = 0; - if(arg.sign()) + if(original_arg.sign()) *res = -*res; return; - case conv_type::exponent_nan: + case cpp_bin_float::exponent_nan: *res = std::numeric_limits::quiet_NaN(); return; - case conv_type::exponent_infinity: + case cpp_bin_float::exponent_infinity: *res = (std::numeric_limits::infinity)(); - if(arg.sign()) + if(original_arg.sign()) *res = -*res; return; } - common_exp_type e = arg.exponent(); static const common_exp_type min_exp_limit = std::numeric_limits::min_exponent - (common_exp_type)cpp_bin_float::digits, digit_base_2, void, Exponent, MinE, MaxE>::bit_count - std::numeric_limits::digits - 2; + common_exp_type e = original_arg.exponent(); e -= cpp_bin_float::digits, digit_base_2, void, Exponent, MinE, MaxE>::bit_count - 1; if(e < min_exp_limit) { @@ -1268,7 +1267,22 @@ inline typename boost::enable_if_c::value>::type eval_con *res = std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : (std::numeric_limits::max)(); return; } + if(original_arg.exponent() < std::numeric_limits::min_exponent - 1) + { + // Result is not zero but is denormalized, in order to avoid double rounding we need to use + // a little trickery to ensure rounding occurs in the right place, and just once, thanks + // to Michael Shatz for this, see: https://svn.boost.org/trac/boost/attachment/ticket/12527 + using default_ops::eval_add; + cpp_bin_float temp(original_arg); + eval_add(temp, original_arg.sign() ? -std::numeric_limits::min() : std::numeric_limits::min()); + eval_convert_to(res, temp); + *res -= original_arg.sign() ? -std::numeric_limits::min() : std::numeric_limits::min(); + return; + } + conv_type arg(original_arg); + e = arg.exponent(); + e -= cpp_bin_float::digits, digit_base_2, void, Exponent, MinE, MaxE>::bit_count - 1; *res = std::ldexp(static_cast(*arg.bits().limbs()), static_cast(e)); for(unsigned i = 1; i < arg.bits().size(); ++i) { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ce2637e1..92be0b98 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -493,7 +493,9 @@ run test_convert_from_float128.cpp # # This take too long to run as a regular part of the tests: # -# run test_cpp_bin_float_round.cpp mpfr gmp ; +run test_cpp_bin_float_round.cpp mpfr gmp ; +explicit test_cpp_bin_float_round ; + run test_cpp_bin_float_conv.cpp ; run test_cpp_bin_float_io.cpp no_eh_support /boost/system//boost_system /boost/chrono//boost_chrono diff --git a/test/test_cpp_bin_float_conv.cpp b/test/test_cpp_bin_float_conv.cpp index e49bf907..ad075841 100644 --- a/test/test_cpp_bin_float_conv.cpp +++ b/test/test_cpp_bin_float_conv.cpp @@ -89,6 +89,37 @@ int main() boost::multiprecision::number > ext_float("1e-646456978"); BOOST_CHECK_EQUAL(ext_float.convert_to(), 0); + q = -(std::numeric_limits::min)(); + BOOST_CHECK_EQUAL(q.convert_to(), -(std::numeric_limits::min)()); + q = -(std::numeric_limits::max)(); + BOOST_CHECK_EQUAL(q.convert_to(), -(std::numeric_limits::max)()); + q = -(std::numeric_limits::denorm_min)(); + BOOST_CHECK_EQUAL(q.convert_to(), -(std::numeric_limits::denorm_min)()); + // See https://svn.boost.org/trac/boost/ticket/12512: + ext_float = boost::multiprecision::number >("-1e-646456978"); + BOOST_CHECK_EQUAL(ext_float.convert_to(), 0); + // + // Check for double rounding when the result would be a denorm. + // See https://svn.boost.org/trac/boost/ticket/12527 + // + cpp_bin_float_50 r1 = ldexp(cpp_bin_float_50(0x8000000000000bffull), -63 - 1023); + double d1 = r1.convert_to(); + double d2 = boost::math::nextafter(d1, d1 < r1 ? DBL_MAX : -DBL_MAX); + BOOST_CHECK(((abs(d1 - r1) <= abs(d2 - r1)))); + r1 = -r1; + d1 = r1.convert_to(); + d2 = boost::math::nextafter(d1, d1 < r1 ? DBL_MAX : -DBL_MAX); + BOOST_CHECK(((abs(d1 - r1) <= abs(d2 - r1)))); + + r1 = ldexp(cpp_bin_float_50(0x8000017f), -31 - 127); + float f1 = r1.convert_to(); + float f2 = boost::math::nextafter(f1, f1 < r1 ? FLT_MAX : -FLT_MAX); + BOOST_CHECK(((abs(f1 - r1) <= abs(f2 - r1)))); + r1 = -r1; + f1 = r1.convert_to(); + f2 = boost::math::nextafter(f1, f1 < r1 ? FLT_MAX : -FLT_MAX); + BOOST_CHECK(((abs(f1 - r1) <= abs(f2 - r1)))); + return boost::report_errors(); }