[Boost-bugs] [Boost C++ Libraries] #11641: boost::multiprecision::fmod does not behave in the same way as std::fmod with neg divisor

Subject: [Boost-bugs] [Boost C++ Libraries] #11641: boost::multiprecision::fmod does not behave in the same way as std::fmod with neg divisor
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2015-09-10 16:29:19


#11641: boost::multiprecision::fmod does not behave in the same way as std::fmod
with neg divisor
-----------------------------------+----------------------------
 Reporter: Rob Stacey <rpstac@…> | Owner: johnmaddock
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: multiprecision
  Version: Boost 1.57.0 | Severity: Problem
 Keywords: |
-----------------------------------+----------------------------
 I have discovered that the replacement fmod for use with the high
 precision types behaves differently to the std::fmod function when using a
 negative divisor.

 My example is boost::multiprecision::fmod(5.5, -3.333...), this gives a
 remainder of -1.1666... whereas std::fmod gives 2.1666... I'm basing this
 expectation on the following references.

 The description of std::fmod
 (http://en.cppreference.com/w/cpp/numeric/math/fmod):

 "Computes the floating-point remainder of the division operation x/y"
 "The returned value has the same sign as x and is less or equal to y in
 magnitude."

 And the description of the boost::multiprecision::fmod
 (http://www.boost.org/doc/libs/1_59_0/libs/multiprecision/doc/html/boost_multiprecision/ref/backendconc.html#boost_multiprecision.ref.backendconc.optional_requirements_on_the_backend_type):

 "Performs the equivalent operation to std::fmod on arguments cb and cb2,
 and store the result in b. Only required when B is an floating-point type.
 The default version of this function is synthesised from other operations
 above."

 The following code illustrates the problem (with the example case that
 exposed it for me):

 {{{
 #!c++
 #include <cmath>
 #include <iostream>
 #include <iomanip>
 #include <limits>
 #include <boost/multiprecision/cpp_dec_float.hpp>

 int main ( int argc , char *argv[] )
 {
   // This is the double precision standard impl.
   double d1 = 11.0 / 2.0;
   double d2 = -10.0 / 3.0;
   double d3 = std::fmod(d1, -d2);

   // this is the boost multiprecision impl.
   boost::multiprecision::cpp_dec_float_100 b_11("11.0");
   boost::multiprecision::cpp_dec_float_100 b_12("2.0");
   boost::multiprecision::cpp_dec_float_100 b_21("-10.0");
   boost::multiprecision::cpp_dec_float_100 b_22("3.0");
   boost::multiprecision::cpp_dec_float_100 b1(b_11 / b_12);
   boost::multiprecision::cpp_dec_float_100 b2(b_21 / b_22);
   boost::multiprecision::cpp_dec_float_100 b3 =
 boost::multiprecision::fmod(b1, -b2);

   std::cout << std::endl << "Boost (Working) =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<boost::multiprecision::cpp_dec_float_100>::max_digits10);
   std::cout << " Dividend: " << b1 << std::endl;
   std::cout << " Divisor : " << -b2 << std::endl;
   std::cout << " Result : " << b3 << std::endl << std::endl;

   std::cout << "Std =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<double>::max_digits10);
   std::cout << " Dividend: " << d1 << std::endl;
   std::cout << " Divisor : " << -d2 << std::endl;
   std::cout << " Result : " << d3 << std::endl << std::endl;

   b3 = boost::multiprecision::fmod(-b1, -b2);
   d3 = std::fmod(-d1, -d2);

   std::cout << "Boost (Working) =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<boost::multiprecision::cpp_dec_float_100>::max_digits10);
   std::cout << " Dividend: " << -b1 << std::endl;
   std::cout << " Divisor : " << -b2 << std::endl;
   std::cout << " Result : " << b3 << std::endl << std::endl;

   std::cout << "Std =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<double>::max_digits10);
   std::cout << " Dividend: " << -d1 << std::endl;
   std::cout << " Divisor : " << -d2 << std::endl;
   std::cout << " Result : " << d3 << std::endl << std::endl;
 b3 = boost::multiprecision::fmod(-b1, b2);
   d3 = std::fmod(-d1, d2);

   std::cout << "Boost (Broken) =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<boost::multiprecision::cpp_dec_float_100>::max_digits10);
   std::cout << " Dividend: " << -b1 << std::endl;
   std::cout << " Divisor : " << b2 << std::endl;
   std::cout << " Result : " << b3 << std::endl << std::endl;

   std::cout << "Std =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<double>::max_digits10);
   std::cout << " Dividend: " << -d1 << std::endl;
   std::cout << " Divisor : " << d2 << std::endl;
   std::cout << " Result : " << d3 << std::endl << std::endl;

   b3 = boost::multiprecision::fmod(b1, b2);
   d3 = std::fmod(d1, d2);

   std::cout << "Boost (Broken) =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<boost::multiprecision::cpp_dec_float_100>::max_digits10);
   std::cout << " Dividend: " << b1 << std::endl;
   std::cout << " Divisor : " << b2 << std::endl;
   std::cout << " Result : " << b3 << std::endl << std::endl;

   std::cout << "Std =>" << std::endl;
   std::cout <<
 std::setprecision(std::numeric_limits<double>::max_digits10);
   std::cout << " Dividend: " << d1 << std::endl;
   std::cout << " Divisor : " << d2 << std::endl;
   std::cout << " Result : " << d3 << std::endl << std::endl;
 }
 }}}
 This outputs (on my system):

 -bash-4.1$ g++ -std=c++11 calc.cpp -o calc
 -bash-4.1$ ./calc

 Boost (Working) =>
  Dividend: 5.5
  Divisor :
 3.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334
  Result :
 2.166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666

 Std =>
  Dividend: 5.5
  Divisor : 3.3333333333333335
  Result : 2.1666666666666665

 Boost (Working) =>
  Dividend: -5.5
  Divisor :
 3.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334
  Result :
 -2.166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666

 Std =>
  Dividend: -5.5
  Divisor : 3.3333333333333335
  Result : -2.1666666666666665

 Boost (Broken) =>
  Dividend: -5.5
  Divisor :
 -3.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334
  Result :
 1.166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668

 Std =>
  Dividend: -5.5
  Divisor : -3.3333333333333335
  Result : -2.1666666666666665

 Boost (Broken) =>
  Dividend: 5.5
  Divisor :
 -3.333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333334
  Result :
 -1.166666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666668

 Std =>
  Dividend: 5.5
  Divisor : -3.3333333333333335
  Result : 2.1666666666666665

 I believe that the root cause is line 933 of
 include/boost/multiprecision/detail/default_ops.hpp
 (https://github.com/boostorg/multiprecision/blob/master/include/boost/multiprecision/detail/default_ops.hpp)

 Changing this from:
 {{{
 #!c++
    if(eval_get_sign(a) < 0)
 }}}

 to
 {{{
 #!c++
    if(eval_get_sign(result) < 0)
 }}}

 fixes the issue for this case, I'm currently trying to prepare a merge
 request but I didn't want to delay someone confirming that this is a issue
 that needs fixing or whether I have misunderstood something.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/11641>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:19 UTC