diff --git a/src/libqof/qof/gnc-rational.cpp b/src/libqof/qof/gnc-rational.cpp index 7e63438382..23f4338fef 100644 --- a/src/libqof/qof/gnc-rational.cpp +++ b/src/libqof/qof/gnc-rational.cpp @@ -20,6 +20,7 @@ * * *******************************************************************/ +#include #include "gnc-rational.hpp" static const gint64 pten[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, @@ -334,6 +335,62 @@ GncRational::round (GncDenom& denom) noexcept return; } +GncRational +GncRational::reduce() const +{ + auto gcd = m_den.gcd(m_num); + if (gcd.isNan() || gcd.isOverflow()) + throw std::overflow_error("Reduce failed, calculation of gcd overflowed."); + return GncRational(m_num / gcd, m_den / gcd); +} + +GncRational +GncRational::round_to_numeric() const +{ + if (m_num.isZero()) + return GncRational(); //Default constructor makes 0/1 + if (!(m_num.isBig() || m_den.isBig())) + return *this; + if (m_num.abs() > m_den) + { + auto quot(m_num / m_den); + if (quot.isBig()) + { + std::ostringstream msg; + msg << " Cannot be represented as a " + << "GncNumeric. Its integer value is too large.\n"; + throw std::overflow_error(msg.str()); + } + GncRational new_rational(*this); + GncRational scratch(1, 1); + auto divisor = static_cast(m_den / (m_num.abs() >> 62)); + GncDenom gnc_denom(new_rational, scratch, divisor, + GNC_HOW_RND_ROUND_HALF_DOWN); + new_rational.round(gnc_denom); + return new_rational; + } + auto quot(m_den / m_num); + if (quot.isBig()) + return GncRational(); //Smaller than can be represented as a GncNumeric + auto divisor = m_den >> 62; + if (m_num.isBig()) + { + GncInt128 oldnum(m_num), num, rem; + oldnum.div(divisor, num, rem); + auto den = m_den / divisor; + num += rem * 2 >= den ? 1 : 0; + GncRational new_rational(num, den); + return new_rational; + } + GncRational new_rational(*this); + GncRational scratch(1, 1); + auto int_div = static_cast(m_den / divisor); + GncDenom gnc_denom(new_rational, scratch, int_div, + GNC_HOW_RND_ROUND_HALF_DOWN); + new_rational.round(gnc_denom); + return new_rational; +} + GncDenom::GncDenom (GncRational& a, GncRational& b, int64_t spec, unsigned int how) noexcept : m_value (spec), diff --git a/src/libqof/qof/gnc-rational.hpp b/src/libqof/qof/gnc-rational.hpp index affe6b2dff..7c59ce5cc0 100644 --- a/src/libqof/qof/gnc-rational.hpp +++ b/src/libqof/qof/gnc-rational.hpp @@ -45,6 +45,20 @@ public: operator gnc_numeric() const noexcept; /** Make a new GncRational with the opposite sign. */ GncRational operator-() const noexcept; +/** + * Reduce this to an equivalent fraction with the least common multiple as the + * denominator. + * + * @return reduced GncRational + */ + GncRational reduce() const; +/** + * Round to fit an int64_t, finding the closest possible approximation. + * + * Throws std::overflow_error if m_den is 1 and m_num is big. + * @return rounded GncRational + */ + GncRational round_to_numeric() const; /** Round/convert this to the denominator provided by d, according to d's * m_round value. */