Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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__);
    }
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
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));
}
Exemplo n.º 5
0
/*
 * 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);
}
Exemplo n.º 6
0
/* 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("");
}
Exemplo n.º 7
0
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;
}
Exemplo n.º 8
0
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 );
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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);
}
Exemplo n.º 11
0
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;
}
Exemplo n.º 12
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;
}
Exemplo n.º 13
0
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);
}
Exemplo n.º 14
0
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);
}
Exemplo n.º 15
0
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;
}
Exemplo n.º 16
0
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);
}
Exemplo n.º 17
0
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;
        }
    }
}
Exemplo n.º 18
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;
        }
    }
}
Exemplo n.º 19
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 (" ");
}
Exemplo n.º 20
0
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);
}
Exemplo n.º 21
0
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;
}
Exemplo n.º 22
0
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);
    }

}
Exemplo n.º 23
0
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);
}
Exemplo n.º 24
0
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;
}
Exemplo n.º 25
0
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;
}
Exemplo n.º 26
0
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);
}
Exemplo n.º 27
0
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;
}
Exemplo n.º 28
0
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);
}
Exemplo n.º 29
0
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;
}