Reimplement gnc_numeric in terms of GncNumeric instead of GncRational.

Except when how has DenomType::exact; that triggers direct use of GncRational
and direct rounding to the specified denominator.
This commit is contained in:
John Ralls 2017-01-30 13:29:48 -08:00
parent c3d22c429f
commit ff7e6a37d5
8 changed files with 255 additions and 356 deletions

View File

@ -1378,7 +1378,8 @@ PrintAmountInternal(char *buf, gnc_numeric val, const GNCPrintAmountInfo *info)
{
rounding.num = 5; /* Limit the denom to 10^13 ~= 2^44, leaving max at ~524288 */
rounding.denom = pow(10, max_dp + 1);
val = gnc_numeric_add(val, rounding, val.denom, GNC_HOW_RND_TRUNC);
val = gnc_numeric_add(val, rounding, val.denom,
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_TRUNC);
if (gnc_numeric_check(val))
{

View File

@ -39,22 +39,12 @@ test_num_print_info (gnc_numeric n, GNCPrintAmountInfo print_info, int line)
const char *s;
gboolean ok, print_ok;
auto msg = "[PrintAmountInternal()] Bad numeric from rounding: GNC_ERROR_OVERFLOW.";
auto log_domain = "gnc.gui";
auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING);
auto check = test_error_struct_new (log_domain, loglevel, msg);
/* Throws overflows during rounding step in xaccPrintAmount when the "fraction" is high. See bug 665707. */
auto hdlr = g_log_set_handler (log_domain, loglevel,
(GLogFunc)test_checked_handler, &check);
s = xaccPrintAmount (n, print_info);
print_ok = (s && s[0] != '\0');
if (!print_ok)
return;
ok = xaccParseAmount (s, print_info.monetary, &n_parsed, NULL);
g_log_remove_handler (log_domain, hdlr);
do_test_args (ok, "parsing failure", __FILE__, __LINE__,
"num: %s, string %s (line %d)", gnc_numeric_to_string (n), s, line);
@ -64,8 +54,6 @@ test_num_print_info (gnc_numeric n, GNCPrintAmountInfo print_info, int line)
"start: %s, string %s, finish: %s (line %d)",
gnc_numeric_to_string (n), s,
gnc_numeric_to_string (n_parsed), line);
test_error_struct_free (check);
}
static void
@ -90,7 +78,8 @@ test_num (gnc_numeric n)
print_info.force_fit = 0;
print_info.round = 0;
n1 = gnc_numeric_convert (n, fraction, GNC_HOW_RND_ROUND_HALF_UP);
n1 = gnc_numeric_convert (n, fraction, GNC_HOW_DENOM_EXACT |
GNC_HOW_RND_ROUND_HALF_UP);
if (gnc_numeric_check(n1))
{
do_test_args((gnc_numeric_check(n1) == GNC_ERROR_OVERFLOW),
@ -133,6 +122,20 @@ static void
run_tests (void)
{
int i;
auto msg1 = "[gnc_numeric_mul()] Cannot be represented as a GncNumeric. Its integer value is too large.\n";
auto msg2 = "[gnc_numeric_mul()] Overflow during rounding.";
auto msg3 = "[convert()] Value too large to represent as int64_t";
const char* log_domain = "qof";
auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_WARNING);
auto check1 = test_error_struct_new (log_domain, loglevel, msg1);
auto check2 = test_error_struct_new (log_domain, loglevel, msg2);
auto check3 = test_error_struct_new (log_domain, loglevel, msg3);
test_add_error(check1);
test_add_error(check2);
test_add_error(check3);
/* Throws overflows during rounding step in xaccPrintAmount when the "fraction" is high. See bug 665707. */
auto hdlr = g_log_set_handler (log_domain, loglevel,
(GLogFunc)test_list_handler, nullptr);
for (i = 0; i < 50; i++)
{
@ -147,10 +150,13 @@ run_tests (void)
IS_VALID_NUM(n1, n);
test_num (n);
n1 = gnc_numeric_mul (n, n, n.denom, GNC_HOW_RND_ROUND_HALF_UP);
n1 = gnc_numeric_mul (n, n, n.denom,
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
IS_VALID_NUM(n1, n);
test_num (n);
}
g_log_remove_handler (log_domain, hdlr);
test_clear_error_list();
}
int

View File

@ -708,9 +708,9 @@ gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
return -1;
}
GncRational an (a), bn (b);
GncNumeric an (a), bn (b);
return (an.m_num * bn.m_den).cmp(bn.m_num * an.m_den);
return an.cmp(bn);
}
@ -768,6 +768,17 @@ gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom,
return(gnc_numeric_equal(aconv, bconv));
}
static int64_t
denom_lcd(gnc_numeric a, gnc_numeric b, int64_t denom, int how)
{
if (denom == GNC_DENOM_AUTO &&
(how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)
{
GncInt128 ad(a.denom), bd(b.denom);
denom = static_cast<int64_t>(ad.lcm(bd));
}
return denom;
}
/* *******************************************************************
* gnc_numeric_add
@ -781,18 +792,39 @@ gnc_numeric_add(gnc_numeric a, gnc_numeric b,
{
return gnc_numeric_error(GNC_ERROR_ARG);
}
GncRational an (a), bn (b);
GncDenom new_denom (an, bn, denom, how);
if (new_denom.m_error)
return gnc_numeric_error (new_denom.m_error);
denom = denom_lcd(a, b, denom, how);
try
{
return static_cast<gnc_numeric>(an.add(bn, new_denom));
if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
{
GncNumeric an (a), bn (b);
auto sum = an + bn;
return convert(sum, denom, how);
}
GncRational ar(a), br(b);
auto sum = ar + br;
sum.round(denom, static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK));
if (sum.m_error)
return gnc_numeric_error(sum.m_error);
if (sum.m_num.isBig() || sum.m_den.isBig() ||
sum.m_num.isOverflow() || sum.m_den.isOverflow() ||
sum.m_num.isNan() || sum.m_den.isNan())
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
return GncNumeric(sum);
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
}
@ -810,10 +842,41 @@ gnc_numeric_sub(gnc_numeric a, gnc_numeric b,
{
return gnc_numeric_error(GNC_ERROR_ARG);
}
nb = b;
nb.num = -nb.num;
return gnc_numeric_add (a, nb, denom, how);
denom = denom_lcd(a, b, denom, how);
try
{
if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
{
GncNumeric an (a), bn (b);
auto sum = an - bn;
return convert(sum, denom, how);
}
GncRational ar(a), br(b);
auto sum = ar - br;
sum.round(denom, static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK));
if (sum.m_error)
return gnc_numeric_error(sum.m_error);
if (sum.m_num.isBig() || sum.m_den.isBig() ||
sum.m_num.isOverflow() || sum.m_den.isOverflow() ||
sum.m_num.isNan() || sum.m_den.isNan())
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
return GncNumeric(sum);
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
}
/* *******************************************************************
@ -828,20 +891,41 @@ gnc_numeric_mul(gnc_numeric a, gnc_numeric b,
{
return gnc_numeric_error(GNC_ERROR_ARG);
}
GncRational an (a), bn (b);
GncDenom new_denom (an, bn, denom, how);
if (new_denom.m_error)
return gnc_numeric_error (new_denom.m_error);
denom = denom_lcd(a, b, denom, how);
try
{
return static_cast<gnc_numeric>(an.mul(bn, new_denom));
}
if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
{
GncNumeric an (a), bn (b);
auto prod = an * bn;
return convert(prod, denom, how);
}
GncRational ar(a), br(b);
auto prod = ar * br;
prod.round(denom, static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK));
if (prod.m_error)
return gnc_numeric_error(prod.m_error);
if (prod.m_num.isBig() || prod.m_den.isBig() ||
prod.m_num.isOverflow() || prod.m_den.isOverflow() ||
prod.m_num.isNan() || prod.m_den.isNan())
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
return GncNumeric(prod);
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
}
@ -857,17 +941,39 @@ gnc_numeric_div(gnc_numeric a, gnc_numeric b,
{
return gnc_numeric_error(GNC_ERROR_ARG);
}
GncRational an (a), bn (b);
GncDenom new_denom (an, bn, denom, how);
if (new_denom.m_error)
return gnc_numeric_error (new_denom.m_error);
denom = denom_lcd(a, b, denom, how);
try
{
return static_cast<gnc_numeric>(an.div(bn, new_denom));
if ((how & GNC_NUMERIC_DENOM_MASK) != GNC_HOW_DENOM_EXACT)
{
GncNumeric an (a), bn (b);
auto quot = an / bn;
return convert(quot, denom, how);
}
GncRational ar(a), br(b);
auto quot = ar / br;
quot.round(denom, static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK));
if (quot.m_error)
return gnc_numeric_error(quot.m_error);
if (quot.m_num.isBig() || quot.m_den.isBig() ||
quot.m_num.isOverflow() || quot.m_den.isOverflow() ||
quot.m_num.isNan() || quot.m_den.isNan())
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
return GncNumeric(quot);
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err) //Divide by zero
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
}
@ -910,18 +1016,7 @@ gnc_numeric_abs(gnc_numeric a)
gnc_numeric
gnc_numeric_convert(gnc_numeric in, int64_t denom, int how)
{
GncRational a (in), b (gnc_numeric_zero());
GncDenom d (a, b, denom, how);
try
{
d.reduce(a);
a.round (d.get(), d.m_round);
return static_cast<gnc_numeric>(a);
}
catch (const std::overflow_error& err)
{
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
return convert(GncNumeric(in), denom, how);
}
@ -941,18 +1036,27 @@ gnc_numeric_reduce(gnc_numeric in)
if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */
return in;
GncRational a (in), b (gnc_numeric_zero());
GncDenom d (a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
try
{
d.reduce(a);
a.round (d.get(), d.m_round);
return static_cast<gnc_numeric>(a);
GncNumeric an (in);
return static_cast<gnc_numeric>(an.reduce());
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
}
@ -968,87 +1072,48 @@ gnc_numeric_reduce(gnc_numeric in)
gboolean
gnc_numeric_to_decimal(gnc_numeric *a, guint8 *max_decimal_places)
{
guint8 decimal_places = 0;
gnc_numeric converted_val;
gint64 fraction;
g_return_val_if_fail(a, FALSE);
if (gnc_numeric_check(*a) != GNC_ERROR_OK)
return FALSE;
converted_val = *a;
if (converted_val.denom <= 0)
int max_places = max_decimal_places == NULL ? 17 : *max_decimal_places;
try
{
converted_val = gnc_numeric_convert(converted_val, 1, GNC_HOW_DENOM_EXACT);
if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
return FALSE;
*a = converted_val;
if (max_decimal_places)
*max_decimal_places = decimal_places;
GncNumeric an (*a);
auto bn = an.to_decimal(max_places);
*a = static_cast<gnc_numeric>(bn);
return TRUE;
}
/* Zero is easily converted. */
if (converted_val.num == 0)
converted_val.denom = 1;
fraction = converted_val.denom;
while (fraction != 1)
catch (const std::exception& err)
{
switch (fraction % 10)
{
case 0:
fraction = fraction / 10;
break;
case 5:
converted_val = gnc_numeric_mul(converted_val,
gnc_numeric_create(2, 2),
GNC_DENOM_AUTO,
GNC_HOW_DENOM_EXACT |
GNC_HOW_RND_NEVER);
if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
return FALSE;
fraction = fraction / 5;
break;
case 2:
case 4:
case 6:
case 8:
converted_val = gnc_numeric_mul(converted_val,
gnc_numeric_create(5, 5),
GNC_DENOM_AUTO,
GNC_HOW_DENOM_EXACT |
GNC_HOW_RND_NEVER);
if (gnc_numeric_check(converted_val) != GNC_ERROR_OK)
return FALSE;
fraction = fraction / 2;
break;
default:
return FALSE;
}
decimal_places += 1;
PWARN("%s", err.what());
return FALSE;
}
if (max_decimal_places)
*max_decimal_places = decimal_places;
*a = converted_val;
return TRUE;
}
gnc_numeric
gnc_numeric_invert(gnc_numeric num)
{
if (num.num == 0)
return gnc_numeric_zero();
return static_cast<gnc_numeric>(GncRational(num).inv());
try
{
return static_cast<gnc_numeric>(GncNumeric(num).inv());
}
catch (const std::overflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
}
/* *******************************************************************
* double_to_gnc_numeric
********************************************************************/
@ -1059,72 +1124,26 @@ gnc_numeric_invert(gnc_numeric num)
gnc_numeric
double_to_gnc_numeric(double in, gint64 denom, gint how)
{
gnc_numeric out;
gint64 int_part = 0;
double frac_part;
gint64 frac_int = 0;
double logval;
double sigfigs;
if (isnan (in) || fabs (in) > 1e18)
return gnc_numeric_error (GNC_ERROR_OVERFLOW);
if ((denom == GNC_DENOM_AUTO) && (how & GNC_HOW_DENOM_SIGFIG))
try
{
if (fabs(in) < 10e-20)
{
logval = 0;
}
else
{
logval = log10(fabs(in));
logval = ((logval > 0.0) ?
(floor(logval) + 1.0) : (ceil(logval)));
}
sigfigs = GNC_HOW_GET_SIGFIGS(how);
if ((denom = powten (sigfigs - logval)) == POWTEN_OVERFLOW)
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK;
GncNumeric an(in);
return convert(an, denom, how);
}
int_part = (gint64)(floor(fabs(in)));
frac_part = in - (double)int_part;
int_part = int_part * denom;
frac_part = frac_part * (double)denom;
switch (how & GNC_NUMERIC_RND_MASK)
catch (const std::overflow_error& err)
{
case GNC_HOW_RND_FLOOR:
frac_int = (gint64)floor(frac_part);
break;
case GNC_HOW_RND_CEIL:
frac_int = (gint64)ceil(frac_part);
break;
case GNC_HOW_RND_TRUNC:
frac_int = (gint64)frac_part;
break;
case GNC_HOW_RND_ROUND:
case GNC_HOW_RND_ROUND_HALF_UP:
frac_int = (gint64)rint(frac_part);
break;
case GNC_HOW_RND_NEVER:
frac_int = (gint64)floor(frac_part);
if (frac_part != (double) frac_int)
{
/* signal an error */
}
break;
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
}
catch (const std::invalid_argument& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
catch (const std::underflow_error& err)
{
PWARN("%s", err.what());
return gnc_numeric_error(GNC_ERROR_ARG);
}
out.num = int_part + frac_int;
out.denom = denom;
return out;
}
/* *******************************************************************
@ -1191,20 +1210,17 @@ gnc_num_dbg_to_string(gnc_numeric n)
gboolean
string_to_gnc_numeric(const gchar* str, gnc_numeric *n)
{
gint64 tmpnum;
gint64 tmpdenom;
if (!str) return FALSE;
tmpnum = g_ascii_strtoll (str, NULL, 0);
str = strchr (str, '/');
if (!str) return FALSE;
str ++;
tmpdenom = g_ascii_strtoll (str, NULL, 0);
n->num = tmpnum;
n->denom = tmpdenom;
return TRUE;
try
{
GncNumeric an(str);
*n = static_cast<gnc_numeric>(an);
return TRUE;
}
catch (const std::exception& err)
{
PWARN("%s", err.what());
return FALSE;
}
}
/* *******************************************************************

View File

@ -245,9 +245,6 @@ typedef enum
*/
#define GNC_DENOM_AUTO 0
/** Use the value 1/n as the denominator of the output value. */
#define GNC_DENOM_RECIPROCAL( a ) (- ( a ))
/** @} */
/** @name Constructors

View File

@ -47,7 +47,7 @@ GncRational::GncRational (gnc_numeric n) noexcept :
GncRational::operator gnc_numeric () const noexcept
{
if (m_num.isOverflow() || m_num.isNan() ||
if (m_num.isOverflow() || m_num.isNan() ||
m_den.isOverflow() || m_den.isNan())
return gnc_numeric_error(GNC_ERROR_OVERFLOW);
if (m_error != GNC_ERROR_OK)
@ -73,11 +73,14 @@ GncRational::operator-() const noexcept
GncRational&
GncRational::inv () noexcept
{
if (m_den < 0)
{
m_num *= -m_den;
m_den = 1;
}
std::swap(m_num, m_den);
GncRational b {1, 1};
GncDenom d {*this, b, INT64_C(0), GNC_HOW_RND_NEVER };
d.reduce(*this);
reduce();
return *this;
}
@ -185,39 +188,6 @@ GncRational::operator/=(GncRational b)
*this = std::move(new_val);
}
GncRational&
GncRational::mul (const GncRational& b, GncDenom& d)
{
*this *= b;
d.reduce(*this);
round (d.get(), d.m_round);
return *this;
}
GncRational&
GncRational::div (GncRational b, GncDenom& d)
{
*this /= b;
d.reduce(*this);
round (d.get(), d.m_round);
return *this;
}
GncRational&
GncRational::add (const GncRational& b, GncDenom& d)
{
*this += b;
d.reduce(*this);
round (d.get(), d.m_round);
return *this;
}
GncRational&
GncRational::sub (const GncRational& b, GncDenom& d)
{
return add(-b, d);
}
void
GncRational::round (GncInt128 new_den, RoundType rtype)
{
@ -263,9 +233,9 @@ GncRational::round (GncInt128 new_den, RoundType rtype)
GncInt128 gcd = new_num.gcd(new_den);
if (!(gcd.isNan() || gcd.isOverflow()))
{
new_num /= gcd;
new_den /= gcd;
remainder /= gcd;
new_num /= gcd;
new_den /= gcd;
remainder /= gcd;
}
/* if that didn't work, shift both num and den down until neither is "big", then
@ -370,10 +340,7 @@ GncRational::round_to_numeric() const
}
GncRational new_rational(*this);
GncRational scratch(1, 1);
auto divisor = static_cast<int64_t>(m_den / (m_num.abs() >> 62));
GncDenom gnc_denom(new_rational, scratch, divisor,
GNC_HOW_RND_ROUND_HALF_DOWN);
new_rational.round(gnc_denom.get(), gnc_denom.m_round);
new_rational.round(m_den / (m_num.abs() >> 62), RoundType::half_down);
return new_rational;
}
auto quot(m_den / m_num);
@ -391,88 +358,6 @@ GncRational::round_to_numeric() const
}
GncRational new_rational(*this);
GncRational scratch(1, 1);
auto int_div = static_cast<int64_t>(m_den / divisor);
GncDenom gnc_denom(new_rational, scratch, int_div,
GNC_HOW_RND_ROUND_HALF_DOWN);
new_rational.round(gnc_denom.get(), gnc_denom.m_round);
new_rational.round(m_den / divisor, RoundType::half_down);
return new_rational;
}
GncDenom::GncDenom (GncRational& a, GncRational& b,
int64_t spec, unsigned int how) noexcept :
m_value (spec),
m_round (static_cast<RoundType>(how & GNC_NUMERIC_RND_MASK)),
m_type (static_cast<DenomType>(how & GNC_NUMERIC_DENOM_MASK)),
m_auto (spec == GNC_DENOM_AUTO),
m_sigfigs ((how & GNC_NUMERIC_SIGFIGS_MASK) >> 8),
m_error (GNC_ERROR_OK)
{
if (!m_auto)
return;
switch (m_type)
{
case DenomType::fixed:
if (a.m_den == b.m_den)
{
m_value = a.m_den;
}
else if (b.m_num == 0)
{
m_value = a.m_den;
b.m_den = a.m_den;
}
else if (a.m_num == 0)
{
m_value = b.m_den;
a.m_den = b.m_den;
}
else
{
m_error = GNC_ERROR_DENOM_DIFF;
}
m_auto = false;
break;
case DenomType::lcd:
m_value = a.m_den.lcm(b.m_den);
m_auto = false;
break;
default:
break;
}
}
void
GncDenom::reduce (const GncRational& a) noexcept
{
if (!m_auto)
return;
switch (m_type)
{
default:
break;
case DenomType::reduce:
m_value = a.m_den / a.m_num.gcd(a.m_den);
break;
case DenomType::sigfigs:
GncInt128 val {};
if (a.m_num.abs() > a.m_den)
val = a.m_num.abs() / a.m_den;
else
val = a.m_den / a.m_num.abs();
unsigned int digits {};
while (val >= 10)
{
++digits;
val /= 10;
}
m_value = (a.m_num.abs() > a.m_den ? powten (m_sigfigs - digits - 1) :
powten (m_sigfigs + digits));
m_auto = false;
break;
}
}

View File

@ -26,7 +26,7 @@
#include "gnc-numeric.h"
#include "gnc-int128.hpp"
struct GncDenom;
class GncNumeric;
enum class RoundType;
enum class DenomType;
@ -68,13 +68,6 @@ public:
/** Round/convert this to the denominator provided by d, according to d's
* m_round value.
*/
/* These are mutators; in other words, they implement the equivalent of
* operators *=, /=, +=, and -=. They return a reference to this for chaining.
*/
GncRational& mul(const GncRational& b, GncDenom& d);
GncRational& div(GncRational b, GncDenom& d);
GncRational& add(const GncRational& b, GncDenom& d);
GncRational& sub(const GncRational& b, GncDenom& d);
void round (GncInt128 new_den, RoundType rtype);
void operator+=(GncRational b);
void operator-=(GncRational b);
@ -93,19 +86,4 @@ 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
{
GncDenom (GncRational& a, GncRational& b, int64_t spec, unsigned int how) noexcept;
void reduce (const GncRational& a) noexcept;
GncInt128 get () const noexcept { return m_value; }
GncInt128 m_value;
RoundType m_round;
DenomType m_type;
bool m_auto;
unsigned int m_sigfigs;
GNCNumericErrorCode m_error;
};
#endif //__GNC_RATIONAL_HPP__

View File

@ -134,9 +134,15 @@ test_gnc_rational_SOURCES = \
$(top_srcdir)/${MODULEPATH}/gnc-rational.cpp \
$(top_srcdir)/${MODULEPATH}/gnc-numeric.cpp \
$(top_srcdir)/${MODULEPATH}/gnc-int128.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-datetime.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-timezone.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-date.cpp \
$(top_srcdir)/${MODULEPATH}/qoflog.cpp \
gtest-gnc-rational.cpp
test_gnc_rational_CPPFLAGS = \
-I${top_srcdir}/src \
-I${top_srcdir}/src/libqof/qof \
-I${GTEST_HEADERS} \
${GLIB_CFLAGS}
@ -155,8 +161,14 @@ test_gnc_numeric_SOURCES = \
$(top_srcdir)/${MODULEPATH}/gnc-rational.cpp \
$(top_srcdir)/${MODULEPATH}/gnc-int128.cpp \
$(top_srcdir)/${MODULEPATH}/gnc-numeric.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-datetime.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-timezone.cpp \
$(top_srcdir)/$(MODULEPATH)/gnc-date.cpp \
$(top_srcdir)/${MODULEPATH}/qoflog.cpp \
gtest-gnc-numeric.cpp
test_gnc_numeric_CPPFLAGS = \
-I${top_srcdir}/src \
-I${top_srcdir}/src/libqof/qof \
-I${GTEST_HEADERS} \
${GLIB_CFLAGS}

View File

@ -419,6 +419,10 @@ TEST(qofint128_functions, divide)
EXPECT_EQ (zero, q);
EXPECT_EQ (big, r);
big.div (big - 1, q, r);
EXPECT_EQ(one, q);
EXPECT_EQ(one, r);
EXPECT_EQ (big, big %= bigger);
EXPECT_EQ (two, bigger /= big);
}