static inline gint64 gnc_numeric_lcd(gnc_numeric a, gnc_numeric b) { qofint128 lcm; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return GNC_ERROR_ARG; } if (b.denom == a.denom) return a.denom; /* Special case: smaller divides smoothly into larger */ if ((b.denom < a.denom) && ((a.denom % b.denom) == 0)) { return a.denom; } if ((a.denom < b.denom) && ((b.denom % a.denom) == 0)) { return b.denom; } lcm = lcm128 (a.denom, b.denom); if (lcm.isbig) return GNC_ERROR_ARG; return lcm.lo; }
static void test_num (gnc_numeric n) { GNCPrintAmountInfo print_info; int fraction; int i; print_info.commodity = NULL; print_info.min_decimal_places = 0; print_info.use_locale = 1; print_info.use_symbol = 0; for (i = 1, fraction = 10; i < 9; i++, fraction *= 10) { gnc_numeric n1; print_info.use_separators = 1; print_info.monetary = 1; print_info.max_decimal_places = i; print_info.force_fit = 0; print_info.round = 0; n1 = gnc_numeric_convert (n, fraction, GNC_HOW_RND_ROUND_HALF_UP); if (gnc_numeric_check(n1)) { do_test_args((gnc_numeric_check(n1) == GNC_ERROR_OVERFLOW), "BAD NUMERIC CONVERSION", __FILE__, __LINE__, "num: %s, fraction: %d", gnc_numeric_to_string(n), fraction); continue; } test_num_print_info (n1, print_info, __LINE__); print_info.monetary = 0; test_num_print_info (n1, print_info, __LINE__); print_info.use_separators = 0; test_num_print_info (n1, print_info, __LINE__); print_info.round = 1; test_num_print_info (n1, print_info, __LINE__); print_info.round = 0; print_info.force_fit = 1; test_num_print_info (n1, print_info, __LINE__); print_info.round = 1; test_num_print_info (n1, print_info, __LINE__); } }
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_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)); }
/* * This will add value to the account-value for acc, creating a new * list object if necessary */ GList *gncAccountValueAdd (GList *list, Account *acc, gnc_numeric value) { GList *li; GncAccountValue *res = NULL; g_return_val_if_fail (acc, list); g_return_val_if_fail (gnc_numeric_check (value) == GNC_ERROR_OK, list); /* Try to find the account in the list */ for (li = list; li; li = li->next) { res = li->data; if (res->account == acc) { res->value = gnc_numeric_add (res->value, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); return list; } } /* Nope, didn't find it. */ res = g_new0 (GncAccountValue, 1); res->account = acc; res->value = value; return g_list_prepend (list, res); }
/* Set the value for the given input amount */ void gnc_tree_util_set_value_for_amount (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input) { gnc_numeric split_rate; gnc_numeric amount; gnc_numeric value, new_value; int denom; ENTER("trans %p and split %p and input is %s", trans, split, gnc_numeric_to_string (input)); if (gnc_numeric_zero_p (input)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE("zero"); return; } amount = xaccSplitGetAmount (split); value = xaccSplitGetValue (split); denom = gtu_sr_get_value_denom (split); split_rate = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (gnc_numeric_check (split_rate) != GNC_ERROR_OK) split_rate = gnc_numeric_create (100,100); new_value = gnc_numeric_mul (input, split_rate, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, new_value); xaccSplitSetAmount (split, input); LEAVE(""); }
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; }
static void gnc_ep_tmpvarhash_check_vals( gpointer key, gpointer value, gpointer user_data ) { gboolean *allVarsHaveValues = (gboolean*)user_data; gnc_numeric *num = (gnc_numeric*)value; *allVarsHaveValues &= ( num && gnc_numeric_check( *num ) != GNC_ERROR_ARG ); }
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); }
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); }
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b) { if (gnc_numeric_check(a)) { /* a is not a valid number, check b */ if (gnc_numeric_check(b)) /* Both invalid, consider them equal */ return TRUE; else /* a invalid, b valid */ return FALSE; } if (gnc_numeric_check(b)) /* a valid, b invalid */ return FALSE; return gnc_numeric_compare (a, b) == 0; }
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b) { gint64 aa, bb; qofint128 l, r; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return 0; } if (a.denom == b.denom) { if (a.num == b.num) return 0; if (a.num > b.num) return 1; return -1; } if ((a.denom > 0) && (b.denom > 0)) { /* Avoid overflows using 128-bit intermediate math */ l = mult128 (a.num, b.denom); r = mult128 (b.num, a.denom); return cmp128 (l, r); } if (a.denom < 0) a.denom *= -1; if (b.denom < 0) b.denom *= -1; /* BUG: Possible overflow here.. Also, doesn't properly deal with * reciprocal denominators. */ aa = a.num * a.denom; bb = b.num * b.denom; if (aa == bb) return 0; if (aa > bb) return 1; return -1; }
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b) { gint64 aa, bb; if (gnc_numeric_check(a) || gnc_numeric_check(b)) { return 0; } if (a.denom == b.denom) { if (a.num == b.num) return 0; if (a.num > b.num) return 1; return -1; } GncNumeric an (a), bn (b); return (an.m_num * bn.m_den).cmp(bn.m_num * an.m_den); }
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 char * gnc_numeric_print(gnc_numeric in) { char * retval; if (gnc_numeric_check(in)) { retval = g_strdup_printf("<ERROR> [%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]", in.num, in.denom); } else { retval = g_strdup_printf("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT "]", in.num, in.denom); } return retval; }
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); }
gboolean gnc_numeric_positive_p(gnc_numeric a) { if (gnc_numeric_check(a)) { return 0; } else { if ((a.num > 0) && (a.denom != 0)) { return 1; } else { return 0; } } }
gboolean gnc_numeric_zero_p(gnc_numeric a) { if (gnc_numeric_check(a)) { return 0; } else { if ((a.num == 0) && (a.denom != 0)) { return 1; } else { return 0; } } }
void xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu) { gnc_numeric src_amt, src_val; SplitList *node; if (FALSE == is_subsplit (split)) return; ENTER (" "); /* Get 'price' of the indicated split */ src_amt = xaccSplitGetAmount (split); src_val = xaccSplitGetValue (split); /* Loop over splits, adjust each so that it has the same * ratio (i.e. price). Change the value to get things * right; do not change the amount */ for (node = split->parent->splits; node; node = node->next) { Split *s = node->data; Transaction *txn = s->parent; gnc_numeric dst_amt, dst_val, target_val; gnc_numeric frac, delta; int scu; /* Skip the reference split */ if (s == split) continue; scu = gnc_commodity_get_fraction (txn->common_currency); dst_amt = xaccSplitGetAmount (s); dst_val = xaccSplitGetValue (s); frac = gnc_numeric_div (dst_amt, src_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); target_val = gnc_numeric_mul (frac, src_val, scu, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP); if (gnc_numeric_check (target_val)) { PERR ("Numeric overflow of value\n" "\tAcct=%s txn=%s\n" "\tdst_amt=%s src_val=%s src_amt=%s\n", xaccAccountGetName (s->acc), xaccTransGetDescription(txn), gnc_num_dbg_to_string(dst_amt), gnc_num_dbg_to_string(src_val), gnc_num_dbg_to_string(src_amt)); continue; } /* If the required price changes are 'small', do nothing. * That is a case that the user will have to deal with * manually. This routine is really intended only for * a gross level of synchronization. */ delta = gnc_numeric_sub_fixed (target_val, dst_val); delta = gnc_numeric_abs (delta); if (maxmult * delta.num < delta.denom) continue; /* If the amount is small, pass on that too */ if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue; /* Make the actual adjustment */ xaccTransBeginEdit (txn); xaccSplitSetValue (s, target_val); xaccTransCommitEdit (txn); } LEAVE (" "); }
static void gsslrtma_populate_tree_store(GncSxSlrTreeModelAdapter *model) { GtkTreeIter sx_tree_iter; GList *sx_iter; int instances_index = -1; for (sx_iter = model->instances->sx_instance_list; sx_iter != NULL; sx_iter = sx_iter->next) { GncSxInstances *instances = (GncSxInstances*)sx_iter->data; char last_occur_date_buf[MAX_DATE_LENGTH+1]; { const GDate *last_occur = xaccSchedXactionGetLastOccurDate(instances->sx); if (last_occur == NULL || !g_date_valid(last_occur)) { g_stpcpy(last_occur_date_buf, _("Never")); } else { qof_print_gdate(last_occur_date_buf, MAX_DATE_LENGTH, last_occur); } } if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(model->real), &sx_tree_iter, NULL, ++instances_index)) { gtk_tree_store_append(model->real, &sx_tree_iter, NULL); } gtk_tree_store_set(model->real, &sx_tree_iter, SLR_MODEL_COL_NAME, xaccSchedXactionGetName(instances->sx), SLR_MODEL_COL_INSTANCE_STATE, NULL, SLR_MODEL_COL_VARAIBLE_VALUE, NULL, SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE, SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE, SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE, -1); // Insert instance information { GList *inst_iter; GtkTreeIter inst_tree_iter; char instance_date_buf[MAX_DATE_LENGTH+1]; int instance_index = -1; for (inst_iter = instances->instance_list; inst_iter != NULL; inst_iter = inst_iter->next) { GncSxInstance *inst = (GncSxInstance*)inst_iter->data; qof_print_gdate(instance_date_buf, MAX_DATE_LENGTH, &inst->date); if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(model->real), &inst_tree_iter, &sx_tree_iter, ++instance_index)) { gtk_tree_store_append(model->real, &inst_tree_iter, &sx_tree_iter); } gtk_tree_store_set(model->real, &inst_tree_iter, SLR_MODEL_COL_NAME, instance_date_buf, SLR_MODEL_COL_INSTANCE_STATE, _(gnc_sx_instance_state_names[inst->state]), SLR_MODEL_COL_VARAIBLE_VALUE, NULL, SLR_MODEL_COL_INSTANCE_VISIBILITY, TRUE, SLR_MODEL_COL_VARIABLE_VISIBILITY, FALSE, SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, inst->state != SX_INSTANCE_STATE_CREATED, -1); // Insert variable information { GList *vars = NULL, *var_iter; GtkTreeIter var_tree_iter; gint visible_variable_index = -1; vars = gnc_sx_instance_get_variables(inst); for (var_iter = vars; var_iter != NULL; var_iter = var_iter->next) { GncSxVariable *var = (GncSxVariable*)var_iter->data; GString *tmp_str; if (!var->editable) continue; if (gnc_numeric_check(var->value) == GNC_ERROR_OK) { _var_numeric_to_string(&var->value, &tmp_str); } else { tmp_str = g_string_new(_("(Need Value)")); } if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(model->real), &var_tree_iter, &inst_tree_iter, ++visible_variable_index)) { gtk_tree_store_append(model->real, &var_tree_iter, &inst_tree_iter); } gtk_tree_store_set(model->real, &var_tree_iter, SLR_MODEL_COL_NAME, var->name, SLR_MODEL_COL_INSTANCE_STATE, NULL, SLR_MODEL_COL_VARAIBLE_VALUE, tmp_str->str, SLR_MODEL_COL_INSTANCE_VISIBILITY, FALSE, SLR_MODEL_COL_VARIABLE_VISIBILITY, TRUE, SLR_MODEL_COL_INSTANCE_STATE_SENSITIVITY, FALSE - 1); g_string_free(tmp_str, TRUE); } g_list_free(vars); _consume_excess_rows(model->real, visible_variable_index, &inst_tree_iter, &var_tree_iter); } } // if there are more instance iters, remove _consume_excess_rows(model->real, instance_index, &sx_tree_iter, &inst_tree_iter); } } _consume_excess_rows(model->real, instances_index, NULL, &sx_tree_iter); }
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; }
static void check_add_subtract_overflow (void) { int i; for (i = 0; i < NREPS; i++) { /* Div to avoid addition overflows; we're looking for lcd conversion overflows here. */ int exp_a = rand () % 1000; int exp_b = rand () % 1000; gint64 bin_deno_a = (exp_a == 0 ? 1 : exp_a); gint64 bin_deno_b = (exp_b == 0 ? 1 : exp_b); /* int exp_a = rand () % 11; int exp_b = rand () % 11; gint64 bin_deno_a = (1 << exp_a); gint64 bin_deno_b = (1 << exp_a); */ gint64 dec_deno_a = powten (exp_a % 7); gint64 dec_deno_b = powten (exp_b % 7); gint64 na = get_random_gint64 () % (1000000 * dec_deno_a); gint64 nb = get_random_gint64 () % (1000000 * dec_deno_b); gnc_numeric result; GNCNumericErrorCode err; gchar *errmsg; gnc_numeric ba = gnc_numeric_create(na, bin_deno_a); gnc_numeric bb = gnc_numeric_create(nb, bin_deno_b); gnc_numeric da = gnc_numeric_create(na, dec_deno_a); gnc_numeric db = gnc_numeric_create(nb, dec_deno_b); gchar *ba_str = gnc_numeric_to_string (ba); gchar *bb_str = gnc_numeric_to_string (bb); gchar *da_str = gnc_numeric_to_string (da); gchar *db_str = gnc_numeric_to_string (db); /* Add */ result = gnc_numeric_add(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_add(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_add(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_add(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); /* Subtract */ result = gnc_numeric_sub(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_sub(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_sub(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); result = gnc_numeric_sub(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); err = gnc_numeric_check (result); errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str, gnc_numeric_errorCode_to_string (err)); do_test (err == 0, errmsg); g_free (errmsg); g_free (ba_str); g_free (bb_str); g_free (da_str); g_free (db_str); } }
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; }
gboolean gnc_exp_parser_parse_separate_vars (const char * expression, gnc_numeric *value_p, char **error_loc_p, GHashTable *varHash ) { parser_env_ptr pe; var_store_ptr vars; struct lconv *lc; var_store result; char * error_loc; ParserNum *pnum; if (expression == NULL) return FALSE; if (!parser_inited) gnc_exp_parser_real_init ( (varHash == NULL) ); result.variable_name = NULL; result.value = NULL; result.next_var = NULL; vars = make_predefined_variables (); if ( varHash != NULL ) { g_hash_table_foreach( varHash, make_predefined_vars_from_external_helper, &vars); } lc = gnc_localeconv (); pe = init_parser (vars, lc->mon_decimal_point, lc->mon_thousands_sep, trans_numeric, numeric_ops, negate_numeric, g_free, func_op); error_loc = parse_string (&result, expression, pe); pnum = result.value; if (error_loc == NULL) { if (gnc_numeric_check (pnum->value)) { if (error_loc_p != NULL) *error_loc_p = (char *) expression; last_error = NUMERIC_ERROR; } else { if (pnum) { if (value_p) *value_p = gnc_numeric_reduce (pnum->value); if (!result.variable_name) g_free (pnum); } if (error_loc_p != NULL) *error_loc_p = NULL; last_error = PARSER_NO_ERROR; } } else { if (error_loc_p != NULL) *error_loc_p = error_loc; last_error = get_parse_error (pe); } if ( varHash != NULL ) { var_store_ptr newVars; gpointer maybeKey, maybeValue; gnc_numeric *numericValue; newVars = parser_get_vars( pe ); for ( ; newVars ; newVars = newVars->next_var ) { pnum = newVars->value; if ( g_hash_table_lookup_extended( varHash, newVars->variable_name, &maybeKey, &maybeValue ) ) { g_hash_table_remove( varHash, maybeKey ); g_free( maybeKey ); g_free( maybeValue ); } numericValue = g_new0( gnc_numeric, 1 ); *numericValue = ((ParserNum*)newVars->value)->value; // WTF? // numericValue = NULL; g_hash_table_insert( varHash, g_strdup(newVars->variable_name), numericValue ); } } else { update_variables (vars); } free_predefined_variables (vars); exit_parser (pe); return last_error == PARSER_NO_ERROR; }
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 gboolean gnc_payment_window_check_payment (PaymentWindow *pw) { const char *conflict_msg = NULL; Account *post, *acc; gnc_numeric amount_deb, amount_cred; gboolean enable_xfer_acct = TRUE; GtkTreeSelection *selection; if (!pw) return FALSE; /* Verify the "post" account */ if (!pw->post_acct) { conflict_msg = _("You must enter a valid account name for posting."); goto update_cleanup; } /* Verify the user has selected an owner */ gnc_owner_get_owner (pw->owner_choice, &(pw->owner)); if (!gncOwnerIsValid(&pw->owner)) { conflict_msg = _("You must select a company for payment processing."); goto update_cleanup; } /* Test the total amount */ amount_deb = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_debit_edit)); amount_cred = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_credit_edit)); pw->amount_tot = gnc_numeric_sub (amount_cred, amount_deb, gnc_commodity_get_fraction (xaccAccountGetCommodity (pw->post_acct)), GNC_HOW_RND_ROUND_HALF_UP); if (gnc_numeric_check (pw->amount_tot) || gnc_numeric_zero_p (pw->amount_tot)) { enable_xfer_acct = FALSE; } else { /* Verify the user has selected a transfer account */ pw->xfer_acct = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(pw->acct_tree)); if (!pw->xfer_acct) { conflict_msg = _("You must select a transfer account from the account tree."); } } update_cleanup: gtk_widget_set_sensitive (pw->acct_tree, enable_xfer_acct); /* Check if there are issues preventing a successful payment */ gtk_widget_set_tooltip_text (pw->payment_warning, conflict_msg); if (conflict_msg) { gtk_widget_show (pw->payment_warning); gtk_widget_set_sensitive (pw->ok_button, FALSE); return FALSE; } else { gtk_widget_hide (pw->payment_warning); gtk_widget_set_sensitive (pw->ok_button, TRUE); } return TRUE; }
void xaccSplitScrub (Split *split) { Account *account; Transaction *trans; gnc_numeric value, amount; gnc_commodity *currency, *acc_commodity; int scu; if (!split) return; ENTER ("(split=%p)", split); trans = xaccSplitGetParent (split); if (!trans) { LEAVE("no trans"); return; } account = xaccSplitGetAccount (split); /* If there's no account, this split is an orphan. * We need to fix that first, before proceeding. */ if (!account) { xaccTransScrubOrphans (trans); account = xaccSplitGetAccount (split); } /* Grrr... the register gnc_split_register_load() line 203 of * src/register/ledger-core/split-register-load.c will create * free-floating bogus transactions. Ignore these for now ... */ if (!account) { PINFO ("Free Floating Transaction!"); LEAVE ("no account"); return; } /* Split amounts and values should be valid numbers */ value = xaccSplitGetValue (split); if (gnc_numeric_check (value)) { value = gnc_numeric_zero(); xaccSplitSetValue (split, value); } amount = xaccSplitGetAmount (split); if (gnc_numeric_check (amount)) { amount = gnc_numeric_zero(); xaccSplitSetAmount (split, amount); } currency = xaccTransGetCurrency (trans); /* If the account doesn't have a commodity, * we should attempt to fix that first. */ acc_commodity = xaccAccountGetCommodity(account); if (!acc_commodity) { xaccAccountScrubCommodity (account); } if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency)) { LEAVE ("(split=%p) inequiv currency", split); return; } scu = MIN (xaccAccountGetCommoditySCU (account), gnc_commodity_get_fraction (currency)); if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP)) { LEAVE("(split=%p) different values", split); return; } /* * This will be hit every time you answer yes to the dialog "The * current transaction has changed. Would you like to record it. */ PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" " old amount %s %s, new amount %s", trans->description, split->memo, gnc_num_dbg_to_string (xaccSplitGetAmount(split)), gnc_commodity_get_mnemonic (currency), gnc_num_dbg_to_string (xaccSplitGetValue(split))); xaccTransBeginEdit (trans); xaccSplitSetAmount (split, value); xaccTransCommitEdit (trans); LEAVE ("(split=%p)", split); }
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) { 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; return TRUE; } /* Zero is easily converted. */ if (converted_val.num == 0) converted_val.denom = 1; fraction = converted_val.denom; while (fraction != 1) { 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; } if (max_decimal_places) *max_decimal_places = decimal_places; *a = converted_val; return TRUE; }