diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index 87ada6e4d..2695c5a27 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -1299,28 +1299,28 @@ inline BOOST_MP_CXX14_CONSTEXPR void eval_fmod(T& result, const T& a, const T& b eval_floor(n, result); eval_multiply(n, b); eval_subtract(result, a, n); - if (eval_get_sign(result) != 0) + + int c = eval_get_sign(result); + if (c != 0) { // // Sanity check, that due to rounding errors in division, // we haven't accidently calculated the wrong value: // See https://github.com/boostorg/multiprecision/issues/604 for an example. // - if (eval_get_sign(result) == eval_get_sign(b)) - { - if (result.compare(b) >= 0) - { - eval_subtract(result, b); - } - } - else - { - n = b; + // The check is by magnitude, so the comparison reverses when the remainder + // is negative: see https://github.com/boostorg/multiprecision/issues/764 + // + + n = b; + if (c != eval_get_sign(b)) n.negate(); - if (result.compare(n) >= 0) - { - eval_subtract(result, n); - } + int cmp = result.compare(n); + if (c < 0) + cmp = -cmp; + if (cmp >= 0) + { + eval_subtract(result, n); } } } diff --git a/test/git_issue_604.cpp b/test/git_issue_604.cpp index 8d289336d..8bdf10809 100644 --- a/test/git_issue_604.cpp +++ b/test/git_issue_604.cpp @@ -65,6 +65,56 @@ void test() T r = boost::multiprecision::fmod(val1, val2); BOOST_CHECK_EQUAL(r, T(0)); } + // Non-zero remainders with a negative dividend, see issue 764: both conventions + // agree at zero, so the cases above cannot tell them apart. + { + const T val1{-8.5}; + const T val2{2.5}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(-1)); + } + { + const T val1{-8.5}; + const T val2{-2.5}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(-1)); + } + { + const T val1{8.5}; + const T val2{2.5}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(1)); + } + { + const T val1{8.5}; + const T val2{-2.5}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(1)); + } + { + const T val1{0.5}; + const T val2{-1}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(0.5)); + } + { + const T val1{-0.5}; + const T val2{1}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(-0.5)); + } + { + const T val1{-0.5}; + const T val2{-1}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(-0.5)); + } + { + const T val1{0.5}; + const T val2{1}; + T r = boost::multiprecision::fmod(val1, val2); + BOOST_CHECK_EQUAL(r, T(0.5)); + } }