سؤال

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?

هل كانت مفيدة؟

المحلول

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);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top