gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b) { qofint128 l, r; if ((a.denom == b.denom) && (a.denom > 0)) { return (a.num == b.num); } if ((a.denom > 0) && (b.denom > 0)) { // return (a.num*b.denom == b.num*a.denom); l = mult128 (a.num, b.denom); r = mult128 (b.num, a.denom); return equal128 (l, r); #if ALT_WAY_OF_CHECKING_EQUALITY gnc_numeric ra = gnc_numeric_reduce (a); gnc_numeric rb = gnc_numeric_reduce (b); if (ra.denom != rb.denom) return 0; if (ra.num != rb.num) return 0; return 1; #endif } if ((a.denom < 0) && (b.denom < 0)) { l = mult128 (a.num, -a.denom); r = mult128 (b.num, -b.denom); return equal128 (l, r); } else { /* BUG: One of the numbers has a reciprocal denom, and the other does not. I just don't know to handle this case in any reasonably overflow-proof yet simple way. So, this function will simply get it wrong whenever the three multiplies overflow 64-bits. -CAS */ if (a.denom < 0) { return ((a.num * -a.denom * b.denom) == b.num); } else { return (a.num == (b.num * a.denom * -b.denom)); } } return ((a.num * b.denom) == (a.denom * b.num)); }
static void set_one_key (gpointer key, gpointer value, gpointer data) { char *name = key; ParserNum *pnum = value; char *num_str; num_str = gnc_numeric_to_string (gnc_numeric_reduce (pnum->value)); g_key_file_set_string ((GKeyFile *)data, GROUP_NAME, name, num_str); g_free (num_str); }
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)"); }
void gnc_exp_parser_real_init ( gboolean addPredefined ) { gchar *filename, **keys, **key, *str_value; GKeyFile *key_file; gnc_numeric value; if (parser_inited) gnc_exp_parser_shutdown (); variable_bindings = g_hash_table_new (g_str_hash, g_str_equal); /* This comes after the statics have been initialized. Not at the end! */ parser_inited = TRUE; if ( addPredefined ) { filename = gnc_exp_parser_filname(); key_file = gnc_key_file_load_from_file(filename, TRUE, FALSE, NULL); if (key_file) { keys = g_key_file_get_keys(key_file, GROUP_NAME, NULL, NULL); for (key = keys; key && *key; key++) { str_value = g_key_file_get_string(key_file, GROUP_NAME, *key, NULL); if (str_value && string_to_gnc_numeric(str_value, &value)) { gnc_exp_parser_set_value (*key, gnc_numeric_reduce (value)); } } g_strfreev(keys); g_key_file_free(key_file); } g_free(filename); } }
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"); } } }
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; }
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; }