From a8ab907e203ebe185c9237cfa0ccce4efb4dbfdf Mon Sep 17 00:00:00 2001 From: jzmaddock Date: Tue, 1 Feb 2022 19:02:02 +0000 Subject: [PATCH] Ensure remainder has correct sign when the result is zero. Also ensure float128 specializations of eval_signbit are actually called. Change return type of signbit to bool to match std::signbit and update docs. Add test case. Fixes https://github.com/boostorg/multiprecision/issues/426. --- doc/reference_backend_requirements.qbk | 2 +- doc/reference_number.qbk | 4 +- .../multiprecision/detail/default_ops.hpp | 13 +++-- include/boost/multiprecision/float128.hpp | 4 ++ test/Jamfile.v2 | 1 + test/git_issue_426.cpp | 50 +++++++++++++++++++ 6 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 test/git_issue_426.cpp diff --git a/doc/reference_backend_requirements.qbk b/doc/reference_backend_requirements.qbk index ee8da051..7fac05a8 100644 --- a/doc/reference_backend_requirements.qbk +++ b/doc/reference_backend_requirements.qbk @@ -452,7 +452,7 @@ are either trivial or are forwarded to the Boost.Math implementations by default The full list of these functions is: int sign (const ``['number-or-expression-template-type]``&); - int signbit (const ``['number-or-expression-template-type]``&); + bool signbit (const ``['number-or-expression-template-type]``&); ``['number]`` changesign (const ``['number-or-expression-template-type]``&); ``['number]`` copysign (const ``['number-or-expression-template-type]``&, const ``['number-or-expression-template-type]``&); ``['number]`` asinh (const ``['number-or-expression-template-type]``&); diff --git a/doc/reference_number.qbk b/doc/reference_number.qbk index 1484a677..455def21 100644 --- a/doc/reference_number.qbk +++ b/doc/reference_number.qbk @@ -199,7 +199,7 @@ bool isinf (const ``['number-or-expression-template-type]``&); bool isnan (const ``['number-or-expression-template-type]``&); bool isnormal (const ``['number-or-expression-template-type]``&); - int signbit (const ``['number-or-expression-template-type]``&); + bool signbit (const ``['number-or-expression-template-type]``&); bool isgreater (const ``['number-or-expression-template-type]``&, const ``['number-or-expression-template-type]``&); bool isgreaterequal(const ``['number-or-expression-template-type]``&, const ``['number-or-expression-template-type]``&); @@ -623,7 +623,7 @@ that allows arithmetic to be performed on native integer types producing an exte bool isinf (const ``['number-or-expression-template-type]``&); bool isnan (const ``['number-or-expression-template-type]``&); bool isnormal (const ``['number-or-expression-template-type]``&); - int signbit (const ``['number-or-expression-template-type]``&); + bool signbit (const ``['number-or-expression-template-type]``&); bool isgreater (const ``['number-or-expression-template-type]``&, const ``['number-or-expression-template-type]``&); bool isgreaterequal(const ``['number-or-expression-template-type]``&, const ``['number-or-expression-template-type]``&); diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index 33af7166..452cf028 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -1328,6 +1328,11 @@ inline BOOST_MP_CXX14_CONSTEXPR void eval_remquo(T& result, const T& a, const T& eval_convert_to(pi, n); eval_multiply(n, b); eval_subtract(result, a, n); + if (eval_is_zero(result)) + { + if (eval_signbit(a)) + result.negate(); + } } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::value, void>::type eval_remquo(T& result, const T& x, const A& a, int* pi) @@ -2203,16 +2208,16 @@ inline BOOST_MP_CXX14_CONSTEXPR int sign BOOST_PREVENT_MACRO_SUBSTITUTION(const } template -inline BOOST_MP_CXX14_CONSTEXPR int signbit BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) +inline BOOST_MP_CXX14_CONSTEXPR bool signbit BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) { using default_ops::eval_signbit; - return eval_signbit(arg.backend()); + return static_cast(eval_signbit(arg.backend())); } template -inline BOOST_MP_CXX14_CONSTEXPR int signbit BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::detail::expression& arg) +inline BOOST_MP_CXX14_CONSTEXPR bool signbit BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::detail::expression& arg) { using value_type = typename multiprecision::detail::expression::result_type; - return signbit BOOST_PREVENT_MACRO_SUBSTITUTION(value_type(arg)); + return static_cast(signbit BOOST_PREVENT_MACRO_SUBSTITUTION(value_type(arg))); } template inline BOOST_MP_CXX14_CONSTEXPR multiprecision::number changesign BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) diff --git a/include/boost/multiprecision/float128.hpp b/include/boost/multiprecision/float128.hpp index 916be416..0937cabf 100644 --- a/include/boost/multiprecision/float128.hpp +++ b/include/boost/multiprecision/float128.hpp @@ -723,6 +723,8 @@ inline boost::multiprecision::numbergmp mpfr : no ] ] + [ run git_issue_426.cpp : : : [ check-target-builds ../config//has_mpfr : gmp mpfr TEST_MPFR ] [ check-target-builds ../config//has_float128 : quadmath TEST_FLOAT128 ] ] [ run git_issue_277.cpp ] [ run git_issue_313.cpp ] [ compile git_issue_98.cpp : diff --git a/test/git_issue_426.cpp b/test/git_issue_426.cpp new file mode 100644 index 00000000..a905b97b --- /dev/null +++ b/test/git_issue_426.cpp @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 John Maddock. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#ifdef TEST_MPFR +#include +#endif +#ifdef TEST_FLOAT128 +#include +#endif +#include "test.hpp" + +template +void test() +{ + T d = 360; + for (int i = 2; i >= -2; --i) + { + T x = i * d; + T y = remainder(x, d); + if (y == 0) + BOOST_CHECK_EQUAL(signbit(y), signbit(x)); + if (i == 0) + { + x = -x; + y = remainder(x, d); + if (y == 0) + BOOST_CHECK_EQUAL(signbit(y), signbit(x)); + } + } +} + +int main() +{ + test(); + // No signed zero: + //test(); + //test(); +#ifdef TEST_MPFR + test(); +#endif +#ifdef TEST_FLOAT128 + test(); +#endif + return boost::report_errors(); +} + +