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 double_to_gnc_numeric(double in, gint64 denom, gint how) { gnc_numeric out; gint64 int_part = 0; double frac_part; gint64 frac_int = 0; double logval; double sigfigs; if (isnan (in) || fabs (in) > 1e18) return gnc_numeric_error (GNC_ERROR_OVERFLOW); if ((denom == GNC_DENOM_AUTO) && (how & GNC_HOW_DENOM_SIGFIG)) { if (fabs(in) < 10e-20) { logval = 0; } else { logval = log10(fabs(in)); logval = ((logval > 0.0) ? (floor(logval) + 1.0) : (ceil(logval))); } sigfigs = GNC_HOW_GET_SIGFIGS(how); if ((denom = powten (sigfigs - logval)) == POWTEN_OVERFLOW) return gnc_numeric_error(GNC_ERROR_OVERFLOW); how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; } int_part = (gint64)(floor(fabs(in))); frac_part = in - (double)int_part; int_part = int_part * denom; frac_part = frac_part * (double)denom; switch (how & GNC_NUMERIC_RND_MASK) { case GNC_HOW_RND_FLOOR: frac_int = (gint64)floor(frac_part); break; case GNC_HOW_RND_CEIL: frac_int = (gint64)ceil(frac_part); break; case GNC_HOW_RND_TRUNC: frac_int = (gint64)frac_part; break; case GNC_HOW_RND_ROUND: case GNC_HOW_RND_ROUND_HALF_UP: frac_int = (gint64)rint(frac_part); break; case GNC_HOW_RND_NEVER: frac_int = (gint64)floor(frac_part); if (frac_part != (double) frac_int) { /* signal an error */ } break; } out.num = int_part + frac_int; out.denom = denom; return out; }