From 7ebd9dfd9b0bc463642b065036a3cd6947a74431 Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Mon, 25 Jul 2016 18:57:39 +0100 Subject: [PATCH] Fix fencepost error in rational->float conversion. Add some test cases for the issue. Fixes https://svn.boost.org/trac/boost/ticket/12327. --- .../detail/generic_interconvert.hpp | 6 ++-- test/test_rat_float_interconv.cpp | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/boost/multiprecision/detail/generic_interconvert.hpp b/include/boost/multiprecision/detail/generic_interconvert.hpp index d1fa028d..aa6d3bf5 100644 --- a/include/boost/multiprecision/detail/generic_interconvert.hpp +++ b/include/boost/multiprecision/detail/generic_interconvert.hpp @@ -310,7 +310,7 @@ typename enable_if_c::value || is_floating_point::value>::type num = -num; } int denom_bits = msb(denom); - int shift = std::numeric_limits::digits + denom_bits - msb(num) + 1; + int shift = std::numeric_limits::digits + denom_bits - msb(num); if(shift > 0) num <<= shift; else if(shift < 0) @@ -318,7 +318,7 @@ typename enable_if_c::value || is_floating_point::value>::type Integer q, r; divide_qr(num, denom, q, r); int q_bits = msb(q); - if(q_bits == std::numeric_limits::digits) + if(q_bits == std::numeric_limits::digits - 1) { // // Round up if 2 * r > denom: @@ -334,7 +334,7 @@ typename enable_if_c::value || is_floating_point::value>::type } else { - BOOST_ASSERT(q_bits == 1 + std::numeric_limits::digits); + BOOST_ASSERT(q_bits == std::numeric_limits::digits); // // We basically already have the rounding info: // diff --git a/test/test_rat_float_interconv.cpp b/test/test_rat_float_interconv.cpp index d7a99f46..ea81adfd 100644 --- a/test/test_rat_float_interconv.cpp +++ b/test/test_rat_float_interconv.cpp @@ -233,12 +233,40 @@ void test_random_rationals() #endif } +#if defined(TEST2) + +void double_spot_tests() +{ + boost::multiprecision::cpp_rational rat = 1; + boost::multiprecision::cpp_rational twiddle(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 54)); + rat += boost::multiprecision::cpp_rational(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 50)); + + double d = rat.convert_to(); + + rat += twiddle; + BOOST_CHECK_EQUAL(d, rat.convert_to()); + rat += twiddle; + // tie: round to even rounds down + BOOST_CHECK_EQUAL(d, rat.convert_to()); + rat += twiddle; + BOOST_CHECK_NE(d, rat.convert_to()); + rat -= twiddle; + BOOST_CHECK_EQUAL(d, rat.convert_to()); + rat += boost::multiprecision::cpp_rational(boost::multiprecision::cpp_int(1), boost::multiprecision::cpp_int(boost::multiprecision::cpp_int(1) << 52)); + // tie, but last bit is now a 1 so we round up: + BOOST_CHECK_NE(d, rat.convert_to()); + +} + +#endif + int main() { using namespace boost::multiprecision; #if defined(TEST1) && !defined(BOOST_MSVC) test_round_trip >, cpp_rational>(); #elif defined(TEST2) + double_spot_tests(); test_round_trip(); #elif defined(TEST3) && !defined(BOOST_MSVC) test_random_rationals >, cpp_rational>();