From ff7e6a37d531f05716e1e1214b84ea7cbe49de60 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 30 Jan 2017 13:29:48 -0800 Subject: [PATCH] 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. --- src/app-utils/gnc-ui-util.c | 3 +- .../test/test-print-parse-amount.cpp | 34 +- src/libqof/qof/gnc-numeric.cpp | 392 +++++++++--------- src/libqof/qof/gnc-numeric.h | 3 - src/libqof/qof/gnc-rational.cpp | 139 +------ src/libqof/qof/gnc-rational.hpp | 24 +- src/libqof/qof/test/Makefile.am | 12 + src/libqof/qof/test/gtest-gnc-int128.cpp | 4 + 8 files changed, 255 insertions(+), 356 deletions(-) diff --git a/src/app-utils/gnc-ui-util.c b/src/app-utils/gnc-ui-util.c index eb0db3d7162..e09bdb7e4b2 100644 --- a/src/app-utils/gnc-ui-util.c +++ b/src/app-utils/gnc-ui-util.c @@ -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)) { diff --git a/src/app-utils/test/test-print-parse-amount.cpp b/src/app-utils/test/test-print-parse-amount.cpp index 728cbcfa338..6d4e7e19586 100644 --- a/src/app-utils/test/test-print-parse-amount.cpp +++ b/src/app-utils/test/test-print-parse-amount.cpp @@ -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(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(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 diff --git a/src/libqof/qof/gnc-numeric.cpp b/src/libqof/qof/gnc-numeric.cpp index 15c1e8b9dc3..34345d7c7c6 100644 --- a/src/libqof/qof/gnc-numeric.cpp +++ b/src/libqof/qof/gnc-numeric.cpp @@ -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(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(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(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(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(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(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(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(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(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(a); + GncNumeric an (in); + return static_cast(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(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(GncRational(num).inv()); + try + { + return static_cast(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(an); + return TRUE; + } + catch (const std::exception& err) + { + PWARN("%s", err.what()); + return FALSE; + } } /* ******************************************************************* diff --git a/src/libqof/qof/gnc-numeric.h b/src/libqof/qof/gnc-numeric.h index c67cba7a52a..784073616ab 100644 --- a/src/libqof/qof/gnc-numeric.h +++ b/src/libqof/qof/gnc-numeric.h @@ -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 diff --git a/src/libqof/qof/gnc-rational.cpp b/src/libqof/qof/gnc-rational.cpp index c00cfabfa7c..95fc33dd730 100644 --- a/src/libqof/qof/gnc-rational.cpp +++ b/src/libqof/qof/gnc-rational.cpp @@ -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(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(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(how & GNC_NUMERIC_RND_MASK)), - m_type (static_cast(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; - } -} diff --git a/src/libqof/qof/gnc-rational.hpp b/src/libqof/qof/gnc-rational.hpp index 0ae3fee03d8..415035fde6d 100644 --- a/src/libqof/qof/gnc-rational.hpp +++ b/src/libqof/qof/gnc-rational.hpp @@ -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 @@ class GncRational /** 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__ diff --git a/src/libqof/qof/test/Makefile.am b/src/libqof/qof/test/Makefile.am index ef4d350ebf2..596157b91b8 100644 --- a/src/libqof/qof/test/Makefile.am +++ b/src/libqof/qof/test/Makefile.am @@ -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} diff --git a/src/libqof/qof/test/gtest-gnc-int128.cpp b/src/libqof/qof/test/gtest-gnc-int128.cpp index edd659d2ebb..30d61914f06 100644 --- a/src/libqof/qof/test/gtest-gnc-int128.cpp +++ b/src/libqof/qof/test/gtest-gnc-int128.cpp @@ -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); }