/* Return the ratio n/d reduced so that there are no common factors. */ static inline gnc_numeric reduce128(qofint128 n, gint64 d) { gint64 t; gint64 num; gint64 denom; gnc_numeric out; qofint128 red; t = rem128 (n, d); num = d; denom = t; /* The strategy is to use Euclid's algorithm */ while (denom > 0) { t = num % denom; num = denom; denom = t; } /* num now holds the GCD (Greatest Common Divisor) */ red = div128 (n, num); if (red.isbig) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } out.num = red.lo; if (red.isneg) out.num = -out.num; out.denom = d / num; return out; }
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how) { if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return gnc_numeric_error(GNC_ERROR_ARG); } GncNumeric an (a), bn (b); GncDenom new_denom (an, bn, denom, how); if (new_denom.m_error) return gnc_numeric_error (new_denom.m_error); return static_cast<gnc_numeric>(an.div(bn, new_denom)); }
gnc_numeric gnc_numeric_reduce(gnc_numeric in) { gint64 t; gint64 num = (in.num < 0) ? (- in.num) : in.num ; gint64 denom = in.denom; gnc_numeric out; if (gnc_numeric_check(in)) { return gnc_numeric_error(GNC_ERROR_ARG); } /* The strategy is to use Euclid's algorithm */ while (denom > 0) { t = num % denom; num = denom; denom = t; } /* num now holds the GCD (Greatest Common Divisor) */ /* All calculations are done on positive num, since it's not * well defined what % does for negative values */ out.num = in.num / num; out.denom = in.denom / num; return out; }
gnc_numeric gnc_numeric_abs(gnc_numeric a) { if (gnc_numeric_check(a)) { return gnc_numeric_error(GNC_ERROR_ARG); } return gnc_numeric_create(ABS(a.num), a.denom); }
gnc_numeric gnc_numeric_neg(gnc_numeric a) { if (gnc_numeric_check(a)) { return gnc_numeric_error(GNC_ERROR_ARG); } return gnc_numeric_create(- a.num, a.denom); }
static GncSxVariable* gnc_sx_variable_new(gchar *name) { GncSxVariable *var = g_new0(GncSxVariable, 1); var->name = g_strdup(name); var->value = gnc_numeric_error(GNC_ERROR_ARG); var->editable = TRUE; return var; }
gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how) { gnc_numeric nb; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return gnc_numeric_error(GNC_ERROR_ARG); } nb = b; nb.num = -nb.num; return gnc_numeric_add (a, nb, denom, how); }
gnc_numeric gnc_numeric_reduce(gnc_numeric in) { if (gnc_numeric_check(in)) { return gnc_numeric_error(GNC_ERROR_ARG); } if (in.denom < 0) /* Negative denoms multiply num, can't be reduced. */ return in; GncNumeric a (in), b (gnc_numeric_zero()); GncDenom d (a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND); a.round (d); return static_cast<gnc_numeric>(a); }
static void variable_value_changed_cb(GtkCellRendererText *cell, const gchar *path, const gchar *value, GncSxSinceLastRunDialog *dialog) { GncSxVariable *var; GncSxInstance *inst; GtkTreeIter tree_iter; gnc_numeric parsed_num; char *endStr = NULL; g_debug("variable to [%s] at path [%s]", value, path); if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(dialog->editing_model), &tree_iter, path)) { g_warning("invalid path [%s]", path); return; } if (!gnc_sx_slr_model_get_instance_and_variable(dialog->editing_model, &tree_iter, &inst, &var)) { g_critical("path [%s] doesn't correspond to a valid variable", path); return; } if (!xaccParseAmount(value, TRUE, &parsed_num, &endStr) || gnc_numeric_check(parsed_num) != GNC_ERROR_OK) { gchar *value_copy = g_strdup(value); g_debug("value=[%s] endStr[%s]", value, endStr); if (strlen(g_strstrip(value_copy)) == 0) { gnc_numeric invalid_num = gnc_numeric_error(GNC_ERROR_ARG); gnc_sx_instance_model_set_variable(dialog->editing_model->instances, inst, var, &invalid_num); } else { g_warning("error parsing value [%s]", value); } g_free(value_copy); return; } gnc_sx_instance_model_set_variable(dialog->editing_model->instances, inst, var, &parsed_num); }
static void check_mult_div (void) { int i, j; gint64 v; gnc_numeric c, d; gnc_numeric amt_a, amt_tot, frac, val_tot, val_a; gnc_numeric a, b; a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(1, 1); check_binary_op (gnc_numeric_create(-100, 100), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s div exact"); a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(-1, 1); check_binary_op (gnc_numeric_create(100, 100), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s div exact"); a = gnc_numeric_create(-100, 100); b = gnc_numeric_create(-1, 1); check_binary_op (gnc_numeric_create(100, 100), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s mult exact"); a = gnc_numeric_create(2, 6); b = gnc_numeric_create(1, 4); check_binary_op (gnc_numeric_create(2, 24), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s for mult exact"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s * %s for mult reduce"); check_binary_op (gnc_numeric_create(8, 100), gnc_numeric_mul(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s * %s for mult 100th's"); check_binary_op (gnc_numeric_create(8, 6), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); check_binary_op (gnc_numeric_create(4, 3), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s / %s for div reduce"); check_binary_op (gnc_numeric_create(133, 100), gnc_numeric_div(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s * %s for div 100th's"); /* Check for math with 2^63 < num*num < 2^64 which previously failed * see https://bugs.gnucash.org/show_bug.cgi?id=144980 */ v = 1000000; a = gnc_numeric_create(1 * v, v); b = gnc_numeric_create(10000000 * v, v); check_binary_op (b, gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s * %s for multiply"); /* Multiply some random numbers. This test presumes that * RAND_MAX is approx 2^32 */ for (i = 0; i < NREPS; i++) { gint64 deno = 1; gint64 na = rand(); gint64 nb = rand(); gint64 ne; /* avoid 0 */ if (nb / 4 == 0) { i--; continue; } /* avoid overflow; */ na /= 2; nb /= 2; ne = na * nb; a = gnc_numeric_create(na, deno); b = gnc_numeric_create(nb, deno); check_binary_op_equal (gnc_numeric_create(ne, 1), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s * %s for mult exact"); /* Force 128-bit math to come into play */ for (j = 1; j < 31; j++) { a = gnc_numeric_create(na << j, 1 << j); b = gnc_numeric_create(nb << j, 1 << j); check_binary_op (gnc_numeric_create(ne, 1), gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s * %s for mult reduce"); } /* Do some hokey random 128-bit division too */ b = gnc_numeric_create(deno, nb); check_binary_op_equal (gnc_numeric_create(ne, 1), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); /* avoid overflow; */ na /= 2; nb /= 2; ne = na * nb; for (j = 1; j < 16; j++) { a = gnc_numeric_create(na << j, 1 << j); b = gnc_numeric_create(1 << j, nb << j); check_binary_op (gnc_numeric_create(ne, 1), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s / %s for div reduce"); } } a = gnc_numeric_create(INT64_C(1173888083434299), 93773); b = gnc_numeric_create(INT64_C(2222554708930978), 89579); /* Dividing the above pair overflows, in that after * the division the denominator won't fit into a * 64-bit quantity. This can be seen from * the factorization into primes: * 1173888083434299 = 3 * 2283317 * 171371749 * (yes, thats a seven and a nine digit prime) * 2222554708930978 = 2 * 1111277354465489 * (yes, that's a sixteen-digit prime number) * 93773 = 79*1187 * 89579 = 67*7*191 * If the rounding method is exact/no-round, then * an overflow error should be signalled; else the * divide routine should shift down the results till * the overflow is eliminated. */ check_binary_op (gnc_numeric_error (GNC_ERROR_OVERFLOW), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_RND_NEVER | GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s / %s for div exact"); check_binary_op (gnc_numeric_create(504548, 1000000), gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s / %s for div round"); /* The below is a 'typical' value calculation: * value_frac = value_tot * amt_frace / amt_tot * and has some typical potential-overflow values. * 82718 = 2 * 59 * 701 * 47497125586 = 2 * 1489 * 15949337 * 69100955 = 5 * 7 * 11 * 179483 * 32005637020 = 4 * 5 * 7 * 43 * 71 * 103 * 727 */ a = gnc_numeric_create (-47497125586LL, 82718); b = gnc_numeric_create (-69100955LL, 55739); c = gnc_numeric_mul (a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); d = gnc_numeric_create (-32005637020LL, 55739); check_binary_op (gnc_numeric_create(-102547458LL, 82718), gnc_numeric_div(c, d, 82718, GNC_HOW_DENOM_EXACT), c, d, "expected %s got %s = %s / %s for div round"); /* If we specify GNC_HOW_RND_NEVER, then we shoukld get an error, * since the exact result won't fit into a 64-bit quantity. */ check_binary_op (gnc_numeric_error (GNC_ERROR_REMAINDER), gnc_numeric_div(c, d, 82718, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER), c, d, "expected %s got %s = %s / %s for div round"); /* A simple irreducible ratio, involving negative numbers */ amt_a = gnc_numeric_create (-6005287905LL, 40595); amt_tot = gnc_numeric_create (-8744187958LL, 40595); frac = gnc_numeric_div (amt_a, amt_tot, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(6005287905LL, 8744187958LL), frac, amt_a, amt_tot, "expected %s got %s = %s / %s for div reduce"); /* Another overflow-prone condition */ val_tot = gnc_numeric_create (-4280656418LL, 19873); val_a = gnc_numeric_mul (frac, val_tot, gnc_numeric_denom(val_tot), GNC_HOW_RND_ROUND | GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(-2939846940LL, 19873), val_a, val_tot, frac, "expected %s got %s = %s * %s for mult round"); frac = gnc_numeric_create (396226789777979LL, 328758834367851752LL); val_tot = gnc_numeric_create (467013515494988LL, 100); val_a = gnc_numeric_mul (frac, val_tot, gnc_numeric_denom(val_tot), GNC_HOW_RND_ROUND | GNC_HOW_DENOM_REDUCE); check_binary_op (gnc_numeric_create(562854124919LL, 100), val_a, val_tot, frac, "expected %s got %s = %s * %s for mult round"); /* Yet another bug from bugzilla ... */ a = gnc_numeric_create (40066447153986554LL, 4518); b = gnc_numeric_create (26703286457229LL, 3192); frac = gnc_numeric_div(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND); check_binary_op (gnc_numeric_create(106007, 100), frac, a, b, "expected %s got %s = %s / %s for mult sigfigs"); }
gnc_numeric gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) { gnc_numeric out; gnc_numeric temp; gint64 temp_bc; gint64 temp_a; gint64 remainder; gint64 sign; gint denom_neg = 0; double ratio, logratio; double sigfigs; qofint128 nume, newm; temp.num = 0; temp.denom = 0; if (gnc_numeric_check(in)) { return gnc_numeric_error(GNC_ERROR_ARG); } if (denom == GNC_DENOM_AUTO) { switch (how & GNC_NUMERIC_DENOM_MASK) { default: case GNC_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */ case GNC_HOW_DENOM_EXACT: return in; break; case GNC_HOW_DENOM_REDUCE: /* reduce the input to a relatively-prime fraction */ return gnc_numeric_reduce(in); break; case GNC_HOW_DENOM_FIXED: if (in.denom != denom) { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } else { return in; } break; case GNC_HOW_DENOM_SIGFIG: ratio = fabs(gnc_numeric_to_double(in)); if (ratio < 10e-20) { logratio = 0; } else { logratio = log10(ratio); logratio = ((logratio > 0.0) ? (floor(logratio) + 1.0) : (ceil(logratio))); } sigfigs = GNC_HOW_GET_SIGFIGS(how); if (fabs(sigfigs - logratio) > 18) return gnc_numeric_error(GNC_ERROR_OVERFLOW); if (sigfigs - logratio >= 0) { denom = (gint64)(pow(10, sigfigs - logratio)); } else { denom = -((gint64)(pow(10, logratio - sigfigs))); } how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; break; } } /* Make sure we need to do the work */ if (in.denom == denom) { return in; } if (in.num == 0) { out.num = 0; out.denom = denom; return out; } /* If the denominator of the input value is negative, get rid of that. */ if (in.denom < 0) { in.num = in.num * (- in.denom); /* BUG: overflow not handled. */ in.denom = 1; } sign = (in.num < 0) ? -1 : 1; /* If the denominator is less than zero, we are to interpret it as * the reciprocal of its magnitude. */ if (denom < 0) { /* XXX FIXME: use 128-bit math here ... */ denom = - denom; denom_neg = 1; temp_a = (in.num < 0) ? -in.num : in.num; temp_bc = in.denom * denom; /* BUG: overflow not handled. */ remainder = temp_a % temp_bc; out.num = temp_a / temp_bc; out.denom = - denom; } else { /* Do all the modulo and int division on positive values to make * things a little clearer. Reduce the fraction denom/in.denom to * help with range errors */ temp.num = denom; temp.denom = in.denom; temp = gnc_numeric_reduce(temp); /* Symbolically, do the following: * out.num = in.num * temp.num; * remainder = out.num % temp.denom; * out.num = out.num / temp.denom; * out.denom = denom; */ nume = mult128 (in.num, temp.num); newm = div128 (nume, temp.denom); remainder = rem128 (nume, temp.denom); if (newm.isbig) { return gnc_numeric_error(GNC_ERROR_OVERFLOW); } out.num = newm.lo; out.denom = denom; } if (remainder) { switch (how & GNC_NUMERIC_RND_MASK) { case GNC_HOW_RND_FLOOR: if (sign < 0) { out.num = out.num + 1; } break; case GNC_HOW_RND_CEIL: if (sign > 0) { out.num = out.num + 1; } break; case GNC_HOW_RND_TRUNC: break; case GNC_HOW_RND_PROMOTE: out.num = out.num + 1; break; case GNC_HOW_RND_ROUND_HALF_DOWN: if (denom_neg) { if ((2 * remainder) > in.denom * denom) { out.num = out.num + 1; } } else if ((2 * remainder) > temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder > (temp.denom / 2))) { out.num = out.num + 1; } break; case GNC_HOW_RND_ROUND_HALF_UP: if (denom_neg) { if ((2 * remainder) >= in.denom * denom) { out.num = out.num + 1; } } else if ((2 * remainder ) >= temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder >= (temp.denom / 2))) { out.num = out.num + 1; } break; case GNC_HOW_RND_ROUND: if (denom_neg) { if ((2 * remainder) > in.denom * denom) { out.num = out.num + 1; } else if ((2 * remainder) == in.denom * denom) { if (out.num % 2) { out.num = out.num + 1; } } } else { if ((2 * remainder ) > temp.denom) { out.num = out.num + 1; } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder > (temp.denom / 2))) { out.num = out.num + 1; } else if ((2 * remainder) == temp.denom) { if (out.num % 2) { out.num = out.num + 1; } } /* check that 2*remainder didn't over-flow */ else if (((2 * remainder) < remainder) && (remainder == (temp.denom / 2))) { if (out.num % 2) { out.num = out.num + 1; } } } break; case GNC_HOW_RND_NEVER: return gnc_numeric_error(GNC_ERROR_REMAINDER); break; } } out.num = (sign > 0) ? out.num : (-out.num); return out; }
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how) { gnc_numeric quotient; qofint128 nume, deno; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return gnc_numeric_error(GNC_ERROR_ARG); } if ((denom == GNC_DENOM_AUTO) && (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if (a.denom == b.denom) { denom = a.denom; } else if (a.denom == 0) { denom = b.denom; } else { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } } if (a.denom < 0) { a.num *= -a.denom; /* BUG: overflow not handled. */ a.denom = 1; } if (b.denom < 0) { b.num *= -b.denom; /* BUG: overflow not handled. */ b.denom = 1; } if (a.denom == b.denom) { quotient.num = a.num; quotient.denom = b.num; } else { gint64 sgn = 1; if (0 > a.num) { sgn = -sgn; a.num = -a.num; } if (0 > b.num) { sgn = -sgn; b.num = -b.num; } nume = mult128(a.num, b.denom); deno = mult128(b.num, a.denom); /* Try to avoid overflow by removing common factors */ if (nume.isbig && deno.isbig) { gnc_numeric ra = gnc_numeric_reduce (a); gnc_numeric rb = gnc_numeric_reduce (b); gint64 gcf_nume = gcf64(ra.num, rb.num); gint64 gcf_deno = gcf64(rb.denom, ra.denom); nume = mult128(ra.num / gcf_nume, rb.denom / gcf_deno); deno = mult128(rb.num / gcf_nume, ra.denom / gcf_deno); } if ((0 == nume.isbig) && (0 == deno.isbig)) { quotient.num = sgn * nume.lo; quotient.denom = deno.lo; goto dive_done; } else if (0 == deno.isbig) { quotient = reduce128 (nume, deno.lo); if (0 == gnc_numeric_check (quotient)) { quotient.num *= sgn; goto dive_done; } } /* If rounding allowed, then shift until there's no * more overflow. The conversion at the end will fix * things up for the final value. */ if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } while (nume.isbig || deno.isbig) { nume = shift128 (nume); deno = shift128 (deno); } quotient.num = sgn * nume.lo; quotient.denom = deno.lo; if (0 == quotient.denom) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } } if (quotient.denom < 0) { quotient.num = -quotient.num; quotient.denom = -quotient.denom; } dive_done: if ((denom == GNC_DENOM_AUTO) && ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; } return gnc_numeric_convert(quotient, denom, how); }
gnc_numeric gnc_numeric_mul(gnc_numeric a, gnc_numeric b, gint64 denom, gint how) { gnc_numeric product, result; qofint128 bignume, bigdeno; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return gnc_numeric_error(GNC_ERROR_ARG); } if ((denom == GNC_DENOM_AUTO) && (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if (a.denom == b.denom) { denom = a.denom; } else if (b.num == 0) { denom = a.denom; } else if (a.num == 0) { denom = b.denom; } else { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } } if ((denom == GNC_DENOM_AUTO) && ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; } if (a.denom < 0) { a.num *= -a.denom; /* BUG: overflow not handled. */ a.denom = 1; } if (b.denom < 0) { b.num *= -b.denom; /* BUG: overflow not handled. */ b.denom = 1; } bignume = mult128 (a.num, b.num); bigdeno = mult128 (a.denom, b.denom); product.num = a.num * b.num; product.denom = a.denom * b.denom; /* If it looks to be overflowing, try to reduce the fraction ... */ if (bignume.isbig || bigdeno.isbig) { gint64 tmp; a = gnc_numeric_reduce (a); b = gnc_numeric_reduce (b); tmp = a.num; a.num = b.num; b.num = tmp; a = gnc_numeric_reduce (a); b = gnc_numeric_reduce (b); bignume = mult128 (a.num, b.num); bigdeno = mult128 (a.denom, b.denom); product.num = a.num * b.num; product.denom = a.denom * b.denom; } /* If it its still overflowing, and rounding is allowed then round */ if (bignume.isbig || bigdeno.isbig) { /* If rounding allowed, then shift until there's no * more overflow. The conversion at the end will fix * things up for the final value. Else overflow. */ if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER) { if (bigdeno.isbig) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } product = reduce128 (bignume, product.denom); if (gnc_numeric_check (product)) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } } else { while (bignume.isbig || bigdeno.isbig) { bignume = shift128 (bignume); bigdeno = shift128 (bigdeno); } product.num = bignume.lo; if (bignume.isneg) product.num = -product.num; product.denom = bigdeno.lo; if (0 == product.denom) { return gnc_numeric_error (GNC_ERROR_OVERFLOW); } } } result = gnc_numeric_convert(product, denom, how); return result; }
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how) { gnc_numeric sum; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return gnc_numeric_error(GNC_ERROR_ARG); } if ((denom == GNC_DENOM_AUTO) && (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if (a.denom == b.denom) { denom = a.denom; } else if (b.num == 0) { denom = a.denom; b.denom = a.denom; } else if (a.num == 0) { denom = b.denom; a.denom = b.denom; } else { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } } if (a.denom < 0) { a.num *= -a.denom; /* BUG: overflow not handled. */ a.denom = 1; } if (b.denom < 0) { b.num *= -b.denom; /* BUG: overflow not handled. */ b.denom = 1; } /* Get an exact answer.. same denominator is the common case. */ if (a.denom == b.denom) { sum.num = a.num + b.num; /* BUG: overflow not handled. */ sum.denom = a.denom; } else { /* We want to do this: * sum.num = a.num*b.denom + b.num*a.denom; * sum.denom = a.denom*b.denom; * but the multiply could overflow. * Computing the LCD minimizes likelihood of overflow */ gint64 lcd; qofint128 ca, cb, cab; lcd = gnc_numeric_lcd(a, b); if (GNC_ERROR_ARG == lcd) { return gnc_numeric_error(GNC_ERROR_OVERFLOW); } ca = mult128 (a.num, lcd / a.denom); if (ca.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW); cb = mult128 (b.num, lcd / b.denom); if (cb.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW); cab = add128 (ca, cb); if (cab.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW); sum.num = cab.lo; if (cab.isneg) sum.num = -sum.num; sum.denom = lcd; } if ((denom == GNC_DENOM_AUTO) && ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; } return gnc_numeric_convert(sum, denom, how); }
static void _wipe_parsed_sx_var(gchar *key, GncSxVariable *var, gpointer unused_user_data) { var->value = gnc_numeric_error(GNC_ERROR_ARG); }
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)) { 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; } 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) { 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; } out.num = int_part + frac_int; out.denom = denom; return out; }