diff --git a/src/libqof/qof/gnc-rational.cpp b/src/libqof/qof/gnc-rational.cpp index 49ecde6e48..8045c95074 100644 --- a/src/libqof/qof/gnc-rational.cpp +++ b/src/libqof/qof/gnc-rational.cpp @@ -83,17 +83,114 @@ GncRational::inv () noexcept return *this; } +GncRational +operator+(GncRational a, GncRational b) +{ + if (a.m_error || b.m_error) + { + if (b.m_error) + return GncRational(0, 1, b.m_error); + return GncRational(0, 1, a.m_error); + } + GncInt128 lcm = a.m_den.lcm(b.m_den); + GncInt128 num(a.m_num * lcm / a.m_den + b.m_num * lcm / b.m_den); + if (lcm.isOverflow() || lcm.isNan() || num.isOverflow() || num.isNan()) + return GncRational(0, 1, GNC_ERROR_OVERFLOW); + GncRational retval(num, lcm); + return retval; +} + +GncRational +operator-(GncRational a, GncRational b) +{ + GncRational retval = -a + b; + return retval; +} + +GncRational +operator*(GncRational a, GncRational b) +{ + if (a.m_error || b.m_error) + { + if (b.m_error) + return GncRational(0, 1, b.m_error); + return GncRational(0, 1, a.m_error); + } + GncInt128 num (a.m_num * b.m_num), den(a.m_den * b.m_den); + if (num.isOverflow() || num.isNan() || den.isOverflow() || den.isNan()) + return GncRational(0, 1, GNC_ERROR_OVERFLOW); + GncRational retval(num, den); + return retval; +} + +GncRational +operator/(GncRational a, GncRational b) +{ + if (a.m_error || b.m_error) + { + if (b.m_error) + return GncRational(0, 1, b.m_error); + return GncRational(0, 1, a.m_error); + } + if (b.m_num.isNeg()) + { + a.m_num = -a.m_num; + b.m_num = -b.m_num; + } + + /* q = (a_num * b_den)/(b_num * a_den). If a_den == b_den they cancel out + * and it's just a_num/b_num. + */ + if (a.m_den == b.m_den) + return GncRational(a.m_num, b.m_num); + + /* Protect against possibly preventable overflow: */ + if (a.m_num.isBig() || a.m_den.isBig() || + b.m_num.isBig() || b.m_den.isBig()) + { + GncInt128 gcd = b.m_den.gcd(a.m_den); + b.m_den /= gcd; + a.m_den /= gcd; + } + + GncInt128 num(a.m_num * b.m_den), den(a.m_den * b.m_num); + if (num.isOverflow() || num.isNan() || den.isOverflow() || den.isNan()) + return GncRational(0, 1, GNC_ERROR_OVERFLOW); + return GncRational(num, den); +} + +void +GncRational::operator+=(GncRational b) +{ + GncRational new_val = *this + b; + *this = std::move(new_val); +} + +void +GncRational::operator-=(GncRational b) +{ + GncRational new_val = *this - b; + *this = std::move(new_val); +} + +void +GncRational::operator*=(GncRational b) +{ + GncRational new_val = *this * b; + *this = std::move(new_val); +} + +void +GncRational::operator/=(GncRational b) +{ + GncRational new_val = *this / b; + *this = std::move(new_val); +} + GncRational& GncRational::mul (const GncRational& b, GncDenom& d) noexcept { - if (m_error || b.m_error) - { - if (b.m_error) - m_error = b.m_error; - return *this; - } - m_num *= b.m_num; - m_den *= b.m_den; + *this *= b; round (d); return *this; } @@ -101,39 +198,7 @@ GncRational::mul (const GncRational& b, GncDenom& d) noexcept GncRational& GncRational::div (GncRational b, GncDenom& d) noexcept { - if (m_error || b.m_error) - { - if (b.m_error) - m_error = b.m_error; - return *this; - } - - if (b.m_num.isNeg()) - { - m_num = -m_num; - b.m_num = -b.m_num; - } - - /* q = (a_num * b_den)/(b_num * a_den). If a_den == b_den they cancel out - * and it's just a_num/b_num. - */ - if (m_den == b.m_den) - { - m_den = b.m_num; - round(d); - return *this; - } - /* Protect against possibly preventable overflow: */ - if (m_num.isBig() || m_den.isBig() || - b.m_num.isBig() || b.m_den.isBig()) - { - GncInt128 gcd = b.m_den.gcd(m_den); - b.m_den /= gcd; - m_den /= gcd; - } - - m_num *= b.m_den; - m_den *= b.m_num; + *this /= b; round (d); return *this; } @@ -141,15 +206,7 @@ GncRational::div (GncRational b, GncDenom& d) noexcept GncRational& GncRational::add (const GncRational& b, GncDenom& d) noexcept { - if (m_error || b.m_error) - { - if (b.m_error) - m_error = b.m_error; - return *this; - } - GncInt128 lcm = m_den.lcm (b.m_den); - m_num = m_num * lcm / m_den + b.m_num * lcm / b.m_den; - m_den = lcm; + *this += b; round (d); return *this; } diff --git a/src/libqof/qof/gnc-rational.hpp b/src/libqof/qof/gnc-rational.hpp index 1772fb930d..ceba87d76a 100644 --- a/src/libqof/qof/gnc-rational.hpp +++ b/src/libqof/qof/gnc-rational.hpp @@ -52,7 +52,10 @@ public: GncRational& div(GncRational b, GncDenom& d) noexcept; GncRational& add(const GncRational& b, GncDenom& d) noexcept; GncRational& sub(const GncRational& b, GncDenom& d) noexcept; - + void operator+=(GncRational b); + void operator-=(GncRational b); + void operator*=(GncRational b); + void operator/=(GncRational b); /** Inverts the number, equivalent of /= {1, 1} */ GncRational& inv() noexcept; @@ -61,6 +64,12 @@ public: GNCNumericErrorCode m_error; }; +GncRational operator+(GncRational a, GncRational b); +GncRational operator-(GncRational a, GncRational b); +GncRational operator*(GncRational a, GncRational b); +GncRational operator/(GncRational a, GncRational b); + + /** Encapsulates the rounding specifications computations. */ struct GncDenom { diff --git a/src/libqof/qof/test/gtest-gnc-rational.cpp b/src/libqof/qof/test/gtest-gnc-rational.cpp index 4005819b40..d9e553a7c8 100644 --- a/src/libqof/qof/test/gtest-gnc-rational.cpp +++ b/src/libqof/qof/test/gtest-gnc-rational.cpp @@ -67,4 +67,74 @@ TEST(gncrational_constructors, test_with_error_code) EXPECT_EQ(456, value.m_den); EXPECT_EQ(GNC_ERROR_OVERFLOW, value.m_error); } + +TEST(gncrational_operators, test_addition) +{ + GncRational a(123456789987654321, 1000000000); + GncRational b(65432198765432198, 100000000); + GncRational c = a + b; + EXPECT_EQ (777778777641976301, c.m_num); + EXPECT_EQ (1000000000, c.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + a += b; + EXPECT_EQ (777778777641976301, a.m_num); + EXPECT_EQ (1000000000, a.m_den); + EXPECT_EQ (GNC_ERROR_OK, a.m_error); +} + +TEST(gncrational_operators, test_subtraction) +{ + GncRational a(123456789987654321, 1000000000); + GncRational b(65432198765432198, 100000000); + GncRational c = a - b; + EXPECT_EQ (530865197666667659, c.m_num); + EXPECT_FALSE(c.m_num.isNeg()); + EXPECT_EQ (1000000000, c.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + c = b - a; + EXPECT_EQ (-530865197666667659, c.m_num); + EXPECT_TRUE(c.m_num.isNeg()); + EXPECT_EQ (1000000000, c.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + a -= b; + EXPECT_EQ (530865197666667659, a.m_num); + EXPECT_FALSE(a.m_num.isNeg()); + EXPECT_EQ (1000000000, a.m_den); + EXPECT_EQ (GNC_ERROR_OK, a.m_error); +} + +TEST(gncrational_operators, test_multiplication) +{ + GncRational a(123456789987654321, 1000000000); + GncRational b(65432198765432198, 100000000); + GncRational c = a * b; + EXPECT_EQ (GncInt128(UINT64_C(437911925765117), + UINT64_C(8081008345983448486)), c.m_num); + EXPECT_EQ (100000000000000000, c.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + a *= b; + EXPECT_EQ (GncInt128(UINT64_C(437911925765117), + UINT64_C(8081008345983448486)), a.m_num); + EXPECT_EQ (100000000000000000, a.m_den); + EXPECT_EQ (GNC_ERROR_OK, a.m_error); +} + +TEST(gncrational_operators, test_division) +{ + GncRational a(123456789987654321, 1000000000); + GncRational b(65432198765432198, 100000000); + GncRational c = a / b; + EXPECT_EQ (GncInt128(UINT64_C(669260), UINT64_C(11059994577585475840)), + c.m_num); + EXPECT_EQ (GncInt128(UINT64_C(3547086), UINT64_C(11115994079396609024)), + c.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + + a /= b; + EXPECT_EQ (GncInt128(UINT64_C(669260), UINT64_C(11059994577585475840)), + a.m_num); + EXPECT_EQ (GncInt128(UINT64_C(3547086), UINT64_C(11115994079396609024)), + a.m_den); + EXPECT_EQ (GNC_ERROR_OK, c.m_error); + }