Search code examples
c++boostroundingmultiprecision

Round boost::multiprecision::mpq_rational to the nearest integer


What is the best way to round boost::multiprecision::mpq_rational to the nearest integer?

An ugly solution is:

#include <boost/multiprecision/gmp.hpp>
using namespace boost::multiprecision;

inline int round_to_int(mpq_rational v)
{
  mpz_int vf_mpz = numerator(v) / denominator(v);
  mpz_t z;
  mpz_init(z);
  mpz_set(z, vf_mpz.backend().data());
  int vf = mpz_get_si(z);
  if (v - vf_mpz < vf_mpz + 1 - v) return vf;
  else return vf + 1;
}

Any better ideas?


Solution

  • At least fix it for negative numbers, as right now -13/7 would round to -1 (when -2 is much nearer).

    Also, since the range should ultimately map onto int, simply:

    template <typename rational>
    inline int round_to_int(rational v)
    {
        v = v + typename rational::number(v.sign()) / 2;
        return v.template convert_to<int>();
    }
    

    This results in more reasonable results, for negative numbers: see it Live On Coliru

    -2   -2.00      -2
    -13/7    -1.86      -2
    -12/7    -1.71      -2
    -11/7    -1.57      -2
    -10/7    -1.43      -1
    -9/7     -1.29      -1
    -8/7     -1.14      -1
    -1   -1.00      -1
    -2   -2.00      -2
    -13/7    -1.86      -2
    -12/7    -1.71      -2
    -11/7    -1.57      -2
    -10/7    -1.43      -1
    -9/7     -1.29      -1
    -8/7     -1.14      -1
    -1   -1.00      -1
    -6/7     -0.86      -1
    -5/7     -0.71      -1
    -4/7     -0.57      -1
    -3/7     -0.43       0
    -2/7     -0.29       0
    -1/7     -0.14       0
    0     0.00       0
    1/7   0.14       0
    2/7   0.29       0
    3/7   0.43       0
    4/7   0.57       1
    5/7   0.71       1
    6/7   0.86       1
    1     1.00       1
    8/7   1.14       1
    9/7   1.29       1
    10/7      1.43       1
    11/7      1.57       2
    12/7      1.71       2
    13/7      1.86       2
    2     2.00       2
    ----------------------
    -2   -2.00      -2
    -3/2     -1.50      -2
    -1   -1.00      -1
    -1/2     -0.50      -1
    0     0.00       0
    1/2   0.50       1
    1     1.00       1
    3/2   1.50       2
    2     2.00       2
    

    Full Code:

    #include <boost/multiprecision/mpfr.hpp>
    #include <boost/multiprecision/number.hpp>
    #include <vector>
    
    using boost::multiprecision::mpq_rational;
    
    template <typename rational>
    inline int round_to_int(rational v)
    {
        v = v + typename rational::number(v.sign()) / 2;
        return v.template convert_to<int>();
    }
    
    int main()
    {
        auto show = [](mpq_rational a) { 
            std::cout << std::right << a << "\t" << std::setw(6) << (double) a << "\t" << std::right << std::setw(6) << round_to_int(a) << '\n';
        };
        std::cout << std::fixed << std::setprecision(2);
    
        for (mpq_rational r(-2); r<=2; r = (r*7+1)/7)
            show(r);
        std::cout << "----------------------\n";
        for (mpq_rational r(-2); r<=2; r = (r*2+1)/2)
            show(r);
    }