Implement basic arithmetic operators for GncRational.

The operators do no rounding or reducing yet.
This commit is contained in:
John Ralls
2017-01-15 12:33:31 -08:00
parent d9aa5e1ad5
commit a852dfb4ef
3 changed files with 187 additions and 51 deletions

View File

@@ -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;
}

View File

@@ -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
{

View File

@@ -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);
}