static void gnc_template_register_save_shares_cell (BasicCell * cell, gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; kvp_frame *kvpf; char *sharesStr = "(x + y)/42"; g_return_if_fail (gnc_basic_cell_has_name (cell, SHRS_CELL)); kvpf = xaccSplitGetSlots (sd->split); /* FIXME: shares cells are numeric by definition. */ DEBUG ("kvp_frame before: %s\n", kvp_frame_to_string (kvpf)); /* sharesStr = gnc_numeric_to_string( sharesStr ); */ kvp_frame_set_slot_path (kvpf, kvp_value_new_string (sharesStr), GNC_SX_ID, GNC_SX_SHARES, NULL); DEBUG ("kvp_frame after: %s\n", kvp_frame_to_string (kvpf)); /* set the shares to an innocuous value */ /* Note that this marks the split dirty */ xaccSplitSetSharePriceAndAmount (sd->split, gnc_numeric_create (0, 1), gnc_numeric_create (0, 1)); }
/* Make sure that the equivalence operator we use for * later tests actually works */ static void check_eq_operator (void) { gnc_numeric a = gnc_numeric_create (42, 58); gnc_numeric b = gnc_numeric_create (42, 58); gnc_numeric c = gnc_numeric_create (40, 58); /* Check strict equivalence and non-equivalence */ do_test (gnc_numeric_eq(a, a), "expected self-equivalence"); do_test (gnc_numeric_eq(a, b), "expected equivalence"); do_test (0 == gnc_numeric_eq(a, c), "expected inequivalence"); }
/* 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(""); }
static void test_dom_tree_to_gnc_numeric(void) { int i; for (i = 0; i < 20; i++) { gchar *message = NULL; message = test_gnc_nums_internal(get_random_gnc_numeric(GNC_DENOM_AUTO)); do_test_args(message == NULL, "dom_tree_to_gnc_numeric", __FILE__, __LINE__, message); } { gchar *message = NULL; message = test_gnc_nums_internal (gnc_numeric_create(18768786810LL, 100000)); do_test_args(message == NULL, "gnc_num 18768786810/100000", __FILE__, __LINE__, message); } }
static void check_double (void) { double flo; gnc_numeric val = gnc_numeric_create (0, 1); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 100000), double_to_gnc_numeric(1.1234567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 10000000), double_to_gnc_numeric(0.011234567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 100), double_to_gnc_numeric(1123.4567890123, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (112346, 10000000000LL), double_to_gnc_numeric(1.1234567890123e-5, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), val, "expected %s = %s double 6 figs"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (961600000, 10000000), double_to_gnc_numeric(96.16, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(9) | GNC_HOW_RND_ROUND), val, "expected %s = %s GncNumeric from 96.16"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (9616000000, 1), double_to_gnc_numeric(9616000000.0, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(9) | GNC_HOW_RND_ROUND), val, "expected %s = %s GncNumeric from 9616000000.0"); flo = gnc_numeric_to_double(gnc_numeric_create(7, 16)); do_test ((0.4375 == flo), "float pt conversion"); }
static void check_neg (void) { gnc_numeric a = gnc_numeric_create(2, 6); gnc_numeric b = gnc_numeric_create(1, 4); gnc_numeric c = gnc_numeric_neg (a); gnc_numeric d = gnc_numeric_neg (b); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-2, 6), c, a, "expected %s got %s = -(%s)"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-1, 4), d, b, "expected %s got %s = -(%s)"); }
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); }
int main(int argc, char ** argv) { gnc_numeric a = gnc_numeric_create(1, 3); gnc_numeric b = gnc_numeric_create(1, 4); gnc_numeric c; gnc_numeric err; c = gnc_numeric_add_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("add 100ths/error : %s + %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_sub_with_error(a, b, 100, GNC_HOW_RND_FLOOR, &err); printf("sub 100ths/error : %s - %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_mul_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("mul 100ths/error : %s * %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); c = gnc_numeric_div_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("div 100ths/error : %s / %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); printf("multiply (EXACT): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT))); printf("multiply (REDUCE): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE))); return 0; }
static void gnc_template_register_save_debcred_cell (BasicCell * cell, gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; SplitRegister *reg = user_data; const char *credit_formula, *debit_formula; char *error_loc; gnc_numeric credit_amount, debit_amount; gboolean parse_result; g_return_if_fail (gnc_basic_cell_has_name (cell, FDEBT_CELL) || gnc_basic_cell_has_name (cell, FCRED_CELL)); if (sd->handled_dc) return; /* amountStr = gnc_numeric_to_string (new_amount); */ credit_formula = gnc_table_layout_get_cell_value (reg->table->layout, FCRED_CELL); /* If the value can be parsed into a numeric result (without any * further variable definitions), store that numeric value * additionally in the kvp. Otherwise store a zero numeric * there.*/ parse_result = gnc_exp_parser_parse_separate_vars(credit_formula, &credit_amount, &error_loc, NULL); if (!parse_result) credit_amount = gnc_numeric_zero(); debit_formula = gnc_table_layout_get_cell_value (reg->table->layout, FDEBT_CELL); /* If the value can be parsed into a numeric result, store that * numeric value additionally. See above comment.*/ parse_result = gnc_exp_parser_parse_separate_vars(debit_formula, &debit_amount, &error_loc, NULL); if (!parse_result) debit_amount = gnc_numeric_zero(); qof_instance_set (QOF_INSTANCE (sd->split), "sx-credit-formula", credit_formula, "sx-credit-numeric", &credit_amount, "sx-debit-formula", debit_formula, "sx-debit-numeric", &debit_amount, NULL); /* set the amount to an innocuous value */ /* Note that this marks the split dirty */ xaccSplitSetValue (sd->split, gnc_numeric_create (0, 1)); sd->handled_dc = TRUE; }
static void gnc_template_register_save_shares_cell (BasicCell * cell, gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; char *sharesStr = "(x + y)/42"; g_return_if_fail (gnc_basic_cell_has_name (cell, SHRS_CELL)); /* FIXME: shares cells are numeric by definition. */ qof_instance_set (QOF_INSTANCE (sd->split), "sx-shares", sharesStr, NULL); /* set the shares to an innocuous value */ /* Note that this marks the split dirty */ xaccSplitSetSharePriceAndAmount (sd->split, gnc_numeric_create (0, 1), gnc_numeric_create (0, 1)); }
static gnc_numeric gnc_split_register_get_rate_cell (SplitRegister *reg, const char *cell_name) { PriceCell *rate_cell; rate_cell = (PriceCell*) gnc_table_layout_get_cell (reg->table->layout, cell_name); if (rate_cell) return gnc_price_cell_get_value (rate_cell); /* Uhh, just return '1' */ return gnc_numeric_create (100, 100); }
static void check_reduce (void) { gnc_numeric one, rone; gnc_numeric four, rfour; gnc_numeric val, rval; /* Check common factor elimination (needed for equality checks) */ one = gnc_numeric_create (1, 1); rone = gnc_numeric_create (1000000, 1000000); rone = gnc_numeric_reduce (rone); do_test (gnc_numeric_eq(one, rone), "reduce to one"); four = gnc_numeric_create (4, 1); rfour = gnc_numeric_create (480, 120); rfour = gnc_numeric_reduce (rfour); do_test (gnc_numeric_eq(four, rfour), "reduce to four"); val = gnc_numeric_create(10023234LL, 334216654LL); rval = gnc_numeric_reduce (val); check_unary_op (gnc_numeric_eq, gnc_numeric_create (5011617, 167108327), rval, val, "check_reduce(1) expected %s got %s = reduce(%s)"); val = gnc_numeric_create(17474724864LL, 136048896LL); rval = gnc_numeric_reduce (val); check_unary_op (gnc_numeric_eq, gnc_numeric_create (4 * 17 * 17, 9), rval, val, "check_reduce(2) expected %s got %s = reduce(%s)"); val = gnc_numeric_create(1024LL, 1099511627776LL); rval = gnc_numeric_reduce (val); check_unary_op (gnc_numeric_eq, gnc_numeric_create (1, 1024 * 1024 * 1024), rval, val, "check_reduce(3): expected %s got %s = reduce(%s)"); }
/* Get the rate from the price db */ static gnc_numeric gtu_sr_get_rate_from_db (gnc_commodity *from, gnc_commodity *to) { GNCPrice *prc; gnc_numeric rate_split; gboolean have_rate = FALSE; QofBook *book = gnc_get_current_book (); /* Do we have a rate allready */ prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), from, to); if (prc) { rate_split = gnc_price_get_value (prc); gnc_price_unref (prc); have_rate = TRUE; } /* Lets try reversing the commodities */ if (!have_rate) { prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), to, from); if (prc) { rate_split = gnc_numeric_div (gnc_numeric_create (100, 100), gnc_price_get_value (prc), GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); gnc_price_unref (prc); have_rate = TRUE; } } /* No rate, set to 1/1 */ if (!have_rate) rate_split = gnc_numeric_create (100, 100); return rate_split; }
/* Copy the values in the financial_info structure to the GUI */ static void fi_to_gui(FinCalcDialog *fcd) { const gnc_commodity *commodity; static char string[64]; gnc_numeric total; gnc_numeric npp; gnc_numeric pmt; int i; if (fcd == NULL) return; npp = gnc_numeric_create (fcd->financial_info.npp, 1); gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(fcd->amounts[PAYMENT_PERIODS]), npp); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[INTEREST_RATE]), fcd->financial_info.ir); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PRESENT_VALUE]), fcd->financial_info.pv); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[PERIODIC_PAYMENT]), fcd->financial_info.pmt); gnc_amount_edit_set_damount (GNC_AMOUNT_EDIT(fcd->amounts[FUTURE_VALUE]), -fcd->financial_info.fv); pmt = double_to_gnc_numeric (fcd->financial_info.pmt, 100000, GNC_HOW_RND_ROUND_HALF_UP); commodity = gnc_default_currency (); total = gnc_numeric_mul (npp, pmt, gnc_commodity_get_fraction (commodity), GNC_HOW_RND_ROUND_HALF_UP); xaccSPrintAmount (string, total, gnc_default_print_info (FALSE)); gtk_label_set_text (GTK_LABEL(fcd->payment_total_label), string); i = normalize_period(&fcd->financial_info.CF); gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->compounding_combo), i); i = normalize_period(&fcd->financial_info.PF); gtk_combo_box_set_active(GTK_COMBO_BOX(fcd->payment_combo), i); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fcd->end_of_period_radio), !fcd->financial_info.bep); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(fcd->discrete_compounding_radio), fcd->financial_info.disc); }
/* Get the rate from the price db */ static gnc_numeric gtu_sr_get_rate_from_db (gnc_commodity *from, gnc_commodity *to) { GNCPrice *prc; QofBook *book = gnc_get_current_book (); prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), from, to); if (!prc) return gnc_numeric_create (100, 100); if (gnc_commodity_equiv(from, gnc_price_get_currency(prc))) return gnc_numeric_invert(gnc_price_get_value(prc)); return gnc_price_get_value(prc); }
static void test_numeric_fcn (QofBook *book, const char *message, void (*set) (GncEmployee *, gnc_numeric), gnc_numeric (*get)(const GncEmployee *)) { GncEmployee *employee = gncEmployeeCreate (book); gnc_numeric num = gnc_numeric_create (17, 1); do_test (!gncEmployeeIsDirty (employee), "test if start dirty"); gncEmployeeBeginEdit (employee); set (employee, num); do_test (gncEmployeeIsDirty (employee), "test dirty later"); gncEmployeeCommitEdit (employee); do_test (gncEmployeeIsDirty (employee), "test dirty after commit"); do_test (gnc_numeric_equal (get (employee), num), message); gncEmployeeSetActive (employee, FALSE); count++; }
static GncSxInstance* gnc_sx_instance_new(GncSxInstances *parent, GncSxInstanceState state, GDate *date, void *temporal_state, gint sequence_num) { GncSxInstance *rtn = g_new0(GncSxInstance, 1); rtn->parent = parent; rtn->orig_state = state; rtn->state = state; g_date_clear(&rtn->date, 1); rtn->date = *date; rtn->temporal_state = gnc_sx_clone_temporal_state(temporal_state); if (! parent->variable_names_parsed) { parent->variable_names = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free); gnc_sx_get_variables(parent->sx, parent->variable_names); g_hash_table_foreach(parent->variable_names, (GHFunc)_wipe_parsed_sx_var, NULL); parent->variable_names_parsed = TRUE; } rtn->variable_bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free); g_hash_table_foreach(parent->variable_names, _clone_sx_var_hash_entry, rtn->variable_bindings); { int instance_i_value; gnc_numeric i_num; GncSxVariable *as_var; instance_i_value = gnc_sx_get_instance_count(rtn->parent->sx, rtn->temporal_state); i_num = gnc_numeric_create(instance_i_value, 1); as_var = gnc_sx_variable_new_full("i", i_num, FALSE); g_hash_table_insert(rtn->variable_bindings, g_strdup("i"), as_var); } return rtn; }
static void test_numeric_fcn (QofBook *book, const char *message, void (*set) (GncEmployee *, gnc_numeric), gnc_numeric (*get)(const GncEmployee *)) { GncEmployee *employee = gncEmployeeCreate (book); gnc_numeric num = gnc_numeric_create (17, 1); do_test (!gncEmployeeIsDirty (employee), "test if start dirty"); gncEmployeeBeginEdit (employee); set (employee, num); /* Employee record should be dirty */ do_test (gncEmployeeIsDirty (employee), "test dirty later"); gncEmployeeCommitEdit (employee); /* Employee record should be not dirty */ /* Skip, because will always fail without a backend. * It's not possible to load a backend in the engine code * without having circular dependencies. */ // do_test (!gncEmployeeIsDirty (employee), "test dirty after commit"); do_test (gnc_numeric_equal (get (employee), num), message); gncEmployeeSetActive (employee, FALSE); count++; }
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code) { return gnc_numeric_create(error_code, 0LL); }
static void gnc_template_register_save_debcred_cell (BasicCell * cell, gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; SplitRegister *reg = user_data; kvp_frame *kvpf; const char *value; char *error_loc; gnc_numeric new_amount; gboolean parse_result; g_return_if_fail (gnc_basic_cell_has_name (cell, FDEBT_CELL) || gnc_basic_cell_has_name (cell, FCRED_CELL)); if (sd->handled_dc) return; kvpf = xaccSplitGetSlots (sd->split); DEBUG ("kvp_frame before: %s\n", kvp_frame_to_string (kvpf)); /* amountStr = gnc_numeric_to_string (new_amount); */ value = gnc_table_layout_get_cell_value (reg->table->layout, FCRED_CELL); kvp_frame_set_slot_path (kvpf, kvp_value_new_string (value), GNC_SX_ID, GNC_SX_CREDIT_FORMULA, NULL); /* If the value can be parsed into a numeric result (without any * further variable definitions), store that numeric value * additionally in the kvp. Otherwise store a zero numeric * there.*/ parse_result = gnc_exp_parser_parse_separate_vars(value, &new_amount, &error_loc, NULL); if (!parse_result) { new_amount = gnc_numeric_zero(); } kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_amount), GNC_SX_ID, GNC_SX_CREDIT_NUMERIC, NULL); value = gnc_table_layout_get_cell_value (reg->table->layout, FDEBT_CELL); kvp_frame_set_slot_path (kvpf, kvp_value_new_string (value), GNC_SX_ID, GNC_SX_DEBIT_FORMULA, NULL); /* If the value can be parsed into a numeric result, store that * numeric value additionally. See above comment.*/ parse_result = gnc_exp_parser_parse_separate_vars(value, &new_amount, &error_loc, NULL); if (!parse_result) { new_amount = gnc_numeric_zero(); } kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_amount), GNC_SX_ID, GNC_SX_DEBIT_NUMERIC, NULL); DEBUG ("kvp_frame after: %s\n", kvp_frame_to_string (kvpf)); /* set the amount to an innocuous value */ /* Note that this marks the split dirty */ xaccSplitSetValue (sd->split, gnc_numeric_create (0, 1)); sd->handled_dc = TRUE; }
/* * This is the logic of computing the total for an Entry, so you know * what values to put into various Splits or to display in the ledger. * In other words, we combine the quantity, unit-price, discount and * taxes together, depending on various flags. * * There are four potental ways to combine these numbers: * Discount: Pre-Tax Post-Tax * Tax : Included Not-Included * * The process is relatively simple: * * 1) compute the agregate price (price*qty) * 2) if taxincluded, then back-compute the agregate pre-tax price * 3) apply discount and taxes in the appropriate order * 4) return the requested results. * * step 2 can be done with agregate taxes; no need to compute them all * unless the caller asked for the tax_value. * * Note that the returned "value" is such that value + tax == "total * to pay," which means in the case of tax-included that the returned * "value" may be less than the agregate price, even without a * discount. If you want to display the tax-included value, you need * to add the value and taxes together. In other words, the value is * the amount the merchant gets; the taxes are the amount the gov't * gets, and the customer pays the sum or value + taxes. * * The SCU is the denominator to convert the value. * * The discount return value is just for entertainment -- you may want * to let a consumer know how much they saved. */ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, const GncTaxTable *tax_table, gboolean tax_included, gnc_numeric discount, GncAmountType discount_type, GncDiscountHow discount_how, int SCU, gnc_numeric *value, gnc_numeric *discount_value, GList **tax_value) { gnc_numeric aggregate; gnc_numeric pretax; gnc_numeric result; gnc_numeric tax; gnc_numeric percent = gnc_numeric_create (100, 1); gnc_numeric tpercent = gnc_numeric_zero (); gnc_numeric tvalue = gnc_numeric_zero (); GList * entries = gncTaxTableGetEntries (tax_table); GList * node; /* Step 1: compute the aggregate price */ aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Step 2: compute the pre-tax aggregate */ /* First, compute the aggregate tpercent and tvalue numbers */ for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; gnc_numeric amount = gncTaxTableEntryGetAmount (entry); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; case GNC_AMT_TYPE_PERCENT: tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry)); } } /* now we need to convert from 5% -> .05 */ tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Next, actually compute the pre-tax aggregate value based on the * taxincluded flag. */ if (tax_table && tax_included) { /* Back-compute the pre-tax aggregate value. * We know that aggregate = pretax + pretax*tpercent + tvalue, so * pretax = (aggregate-tvalue)/(1+tpercent) */ pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); pretax = gnc_numeric_div (pretax, gnc_numeric_add (tpercent, gnc_numeric_create (1, 1), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } else { pretax = aggregate; } /* Step 3: apply discount and taxes in the appropriate order */ /* * There are two ways to apply discounts and taxes. In one way, you * always compute the discount off the pretax number, and compute * the taxes off of either the pretax value or "pretax-discount" * value. In the other way, you always compute the tax on "pretax", * and compute the discount on either "pretax" or "pretax+taxes". * * I don't know which is the "correct" way. */ /* * Type: discount tax * PRETAX pretax pretax-discount * SAMETIME pretax pretax * POSTTAX pretax+tax pretax */ switch (discount_how) { case GNC_DISC_PRETAX: case GNC_DISC_SAMETIME: /* compute the discount from pretax */ if (discount_type == GNC_AMT_TYPE_PERCENT) { discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Figure out when to apply the tax, pretax or pretax-discount */ if (discount_how == GNC_DISC_PRETAX) pretax = result; break; case GNC_DISC_POSTTAX: /* compute discount on pretax+taxes */ if (discount_type == GNC_AMT_TYPE_PERCENT) { gnc_numeric after_tax; tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("unknown DiscountHow value: %d", discount_how); } /* Step 4: return the requested results. */ /* result == amount merchant gets * discount == amount of discount * need to compute taxes (based on 'pretax') if the caller wants it. */ if (discount_value != NULL) { if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND); *discount_value = discount; } if (value != NULL) { if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND); *value = result; } /* Now... Compute the list of tax values (if the caller wants it) */ if (tax_value != NULL) { GList * taxes = NULL; for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; Account *acc = gncTaxTableEntryGetAccount (entry); gnc_numeric amount = gncTaxTableEntryGetAmount (entry); g_return_if_fail (acc); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, amount); break; case GNC_AMT_TYPE_PERCENT: amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, tax); break; default: break; } } *tax_value = taxes; } return; }
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"); }
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); } }
static void check_add_subtract (void) { int i; gnc_numeric a, b, c, d, z; a = gnc_numeric_create(2, 6); b = gnc_numeric_create(1, 4); /* Well, actually 14/24 would be acceptable/better in this case */ check_binary_op (gnc_numeric_create(7, 12), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for add exact"); check_binary_op (gnc_numeric_create(58, 100), gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 100ths (banker's)"); check_binary_op (gnc_numeric_create(5833, 10000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(4) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 4 sig figs"); check_binary_op (gnc_numeric_create(583333, 1000000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 6 sig figs"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for sub exact"); /* We should try something trickier for reduce & lcd */ check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(8, 100), gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)"); /* ------------------------------------------------------------ */ /* This test has failed before */ c = gnc_numeric_neg (a); d = gnc_numeric_neg (b); z = gnc_numeric_zero(); check_binary_op (c, gnc_numeric_add_fixed(z, c), z, c, "expected %s got %s = %s + %s for add fixed"); check_binary_op (d, gnc_numeric_add_fixed(z, d), z, d, "expected %s got %s = %s + %s for add fixed"); /* ------------------------------------------------------------ */ /* Same as above, but with signs reviersed */ a = c; b = d; /* Well, actually 14/24 would be acceptable/better in this case */ check_binary_op (gnc_numeric_create(-7, 12), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for add exact"); check_binary_op (gnc_numeric_create(-58, 100), gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 100ths (banker's)"); check_binary_op (gnc_numeric_create(-5833, 10000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(4) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 4 sig figs"); check_binary_op (gnc_numeric_create(-583333, 1000000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 6 sig figs"); check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for sub exact"); /* We should try something trickier for reduce & lcd */ check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(-8, 100), gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)"); /* ------------------------------------------------------------ */ /* Add and subtract some random numbers */ for (i = 0; i < NREPS; i++) { gnc_numeric e; gint64 deno = rand() + 1; gint64 na = get_random_gint64(); gint64 nb = get_random_gint64(); gint64 ne; /* avoid overflow; */ na /= 2; nb /= 2; a = gnc_numeric_create(na, deno); b = gnc_numeric_create(nb, deno); /* Add */ ne = na + nb; e = gnc_numeric_create(ne, deno); check_binary_op (e, gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for exact addition"); /* Subtract */ ne = na - nb; e = gnc_numeric_create(ne, deno); check_binary_op (e, gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for exact subtraction"); } }
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; }
static void check_rounding (void) { gnc_numeric val; val = gnc_numeric_create(7, 16); check_unary_op (gnc_numeric_eq, gnc_numeric_create (43, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_FLOOR), val, "expected %s got %s = (%s as 100th's floor)"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (44, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_CEIL), val, "expected %s got %s = (%s as 100th's ceiling)"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (43, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_TRUNC), val, "expected %s got %s = (%s as 100th's trunc)"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (44, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); val = gnc_numeric_create(1511, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (151, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); val = gnc_numeric_create(1516, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (152, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); /* Half-values always get rounded to nearest even number */ val = gnc_numeric_create(1515, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (152, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); val = gnc_numeric_create(1525, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (152, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); val = gnc_numeric_create(1535, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (154, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); val = gnc_numeric_create(1545, 1000); check_unary_op (gnc_numeric_eq, gnc_numeric_create (154, 100), gnc_numeric_convert (val, 100, GNC_HOW_RND_ROUND), val, "expected %s got %s = (%s as 100th's round)"); }
static void check_equality_operator (void) { int i, m; gint mult; gint64 f, deno, numer; gnc_numeric big, rbig; gnc_numeric val, mval; gnc_numeric bval, rval; /* Check equality operator for some large numer/denom values */ numer = 1 << 30; numer <<= 30; /* we don't trust cpp to compute 1<<60 correctly */ deno = 1 << 30; deno <<= 20; rbig = gnc_numeric_create (numer, deno); big = gnc_numeric_create (1 << 10, 1); do_test (gnc_numeric_equal(big, rbig), "equal to billion"); big = gnc_numeric_create (1 << 20, 1 << 10); do_test (gnc_numeric_equal(big, rbig), "equal to 1<<20/1<<10"); big = gnc_numeric_create (1 << 30, 1 << 20); do_test (gnc_numeric_equal(big, rbig), "equal to 1<<30/1<<20"); numer = 1 << 30; numer <<= 30; /* we don't trust cpp to compute 1<<60 correctly */ deno = 1 << 30; rbig = gnc_numeric_create (numer, deno); big = gnc_numeric_create (1 << 30, 1); do_test (gnc_numeric_equal(big, rbig), "equal to 1<<30"); numer = 1 << 30; numer <<= 10; big = gnc_numeric_create (numer, 1 << 10); do_test (gnc_numeric_equal(big, rbig), "equal to 1<<40/1<<10"); numer <<= 10; big = gnc_numeric_create (numer, 1 << 20); do_test (gnc_numeric_equal(big, rbig), "equal to 1<<50/1<<20"); /* We assume RAND_MAX is less that 1<<32 */ for (i = 0; i < NREPS; i++) { deno = rand() / 2; mult = rand() / 2; numer = rand() / 2; /* avoid 0 */ if (deno == 0 || mult == 0) { i--; continue; } val = gnc_numeric_create (numer, deno); mval = gnc_numeric_create (numer * mult, deno * mult); /* The reduced version should be equivalent */ bval = gnc_numeric_reduce (val); rval = gnc_numeric_reduce (mval); check_unary_op (gnc_numeric_eq, bval, rval, mval, "expected %s got %s = reduce(%s)"); /* The unreduced versions should be equal */ check_unary_op (gnc_numeric_equal, val, mval, mval, "expected %s = %s"); /* Certain modulo's should be very cleary un-equal; this * helps stop funky modulo-64 aliasing in compares that * might creep in. */ mval.denom >>= 1; mval.num >>= 1; m = 0; f = mval.denom; while (f % 2 == 0) { f >>= 1; m++; } if (1 < m) { gint64 nn = 1 << (32 - m); nn <<= 32; nn += mval.num; val = gnc_numeric_create (2 * nn, 2 * mval.denom); check_unary_op (gnc_numeric_unequal, val, mval, mval, "expected unequality %s != %s"); } } }
void gnc_customer_window_ok_cb (GtkWidget *widget, gpointer data) { CustomerWindow *cw = data; gnc_numeric min, max; gchar *string; /* Check for valid company name */ if (check_entry_nonempty (cw->dialog, cw->company_entry, _("You must enter a company name. " "If this customer is an individual (and not a company) " "you should enter the same value for:\nIdentification " "- Company Name, and\nPayment Address - Name."))) return; /* Make sure we have an address */ if (check_entry_nonempty (cw->dialog, cw->addr1_entry, NULL) && check_entry_nonempty (cw->dialog, cw->addr2_entry, NULL) && check_entry_nonempty (cw->dialog, cw->addr3_entry, NULL) && check_entry_nonempty (cw->dialog, cw->addr4_entry, NULL)) { const char *msg = _("You must enter a billing address."); gnc_error_dialog (cw->dialog, "%s", msg); return; } /* Verify terms, discount, and credit are valid (or empty) */ min = gnc_numeric_zero (); max = gnc_numeric_create (100, 1); if (check_edit_amount (cw->dialog, cw->discount_amount, &min, &max, _("Discount percentage must be between 0-100 " "or you must leave it blank."))) return; if (check_edit_amount (cw->dialog, cw->credit_amount, &min, NULL, _("Credit must be a positive amount or " "you must leave it blank."))) return; /* Set the customer id if one has not been chosen */ if (g_strcmp0 (gtk_entry_get_text (GTK_ENTRY (cw->id_entry)), "") == 0) { string = gncCustomerNextID (cw->book); gtk_entry_set_text (GTK_ENTRY (cw->id_entry), string); g_free(string); } /* Now save it off */ { GncCustomer *customer = cw_get_customer (cw); if (customer) { gnc_ui_to_customer (cw, customer); } cw->created_customer = customer; cw->customer_guid = *guid_null (); } gnc_close_gui_component (cw->component_id); }
void gnc_tree_util_split_reg_save_amount_values (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input) { GncTreeModelSplitReg *model; Account *acc; gnc_numeric new_amount, convrate, amtconv, value; gnc_commodity *curr, *reg_com, *xfer_com; Account *xfer_acc; ENTER("View is %p, trans is %p, split is %p, input is %s", view, trans, split, gnc_numeric_to_string (input)); model = gnc_tree_view_split_reg_get_model_from_view (view); new_amount = input; acc = gnc_tree_model_split_reg_get_anchor (model); xfer_acc = xaccSplitGetAccount (split); xfer_com = xaccAccountGetCommodity (xfer_acc); reg_com = xaccAccountGetCommodity (acc); curr = xaccTransGetCurrency (trans); if (!xaccTransGetRateForCommodity (trans, reg_com, NULL, &convrate)) convrate = gnc_numeric_create (100, 100); amtconv = convrate; if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc)) { /* If we are in an expanded register and the xfer_acc->comm != * reg_acc->comm then we need to compute the convrate here. * Otherwise, we _can_ use the rate_cell! */ if (gnc_commodity_equal (reg_com, xfer_com)) amtconv = xaccTransGetAccountConvRate (trans, acc); } if (xaccTransUseTradingAccounts (trans)) { /* Using currency accounts, the amount is probably really the amount and not the value. */ gboolean is_amount; if (model->type == STOCK_REGISTER2 || model->type == CURRENCY_REGISTER2 || model->type == PORTFOLIO_LEDGER2) { if (xaccAccountIsPriced (xfer_acc) || !gnc_commodity_is_iso (xaccAccountGetCommodity (xfer_acc))) is_amount = FALSE; else is_amount = TRUE; } else { is_amount = TRUE; } if (is_amount) { xaccSplitSetAmount (split, new_amount); if (gnc_tree_util_split_reg_needs_amount (view, split)) { value = gnc_numeric_div (new_amount, amtconv, gnc_commodity_get_fraction (curr), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else xaccSplitSetValue (split, new_amount); } else { xaccSplitSetValue (split, new_amount); } LEAVE(" "); return; } /* How to interpret new_amount depends on our view of this * transaction. If we're sitting in an account with the same * commodity as the transaction, then we can set the Value and then * compute the amount. Otherwise we are setting the "converted * value". This means we need to convert new_amount to the actual * 'value' by dividing by the convrate in order to set the value. */ /* Now compute/set the split value. Amount is in the register * currency but we need to convert to the txn currency. */ if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc)) { /* convert the amount to the Value ... */ value = gnc_numeric_div (new_amount, amtconv, gnc_commodity_get_fraction (curr), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else { xaccSplitSetValue (split, new_amount); } /* Now re-compute the Amount from the Value. We may need to convert * from the Value back to the amount here using the convrate from * earlier. */ value = xaccSplitGetValue (split); if (gnc_tree_util_split_reg_needs_amount (view, split)) { acc = xaccSplitGetAccount (split); new_amount = gnc_numeric_mul (value, convrate, xaccAccountGetCommoditySCU (acc), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, new_amount); } else { xaccSplitSetAmount (split, value); } LEAVE(" "); }