Пример #1
0
/* Return the ratio n/d reduced so that there are no common factors. */
static inline gnc_numeric
reduce128(qofint128 n, gint64 d)
{
    gint64   t;
    gint64   num;
    gint64   denom;
    gnc_numeric out;
    qofint128 red;

    t =  rem128 (n, d);
    num = d;
    denom = t;

    /* 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) */

    red = div128 (n, num);
    if (red.isbig)
    {
        return gnc_numeric_error (GNC_ERROR_OVERFLOW);
    }
    out.num   = red.lo;
    if (red.isneg) out.num = -out.num;
    out.denom = d / num;
    return out;
}
Пример #2
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));
}
Пример #3
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;
}
Пример #4
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);
}
Пример #5
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);
}
Пример #6
0
static GncSxVariable*
gnc_sx_variable_new(gchar *name)
{
    GncSxVariable *var = g_new0(GncSxVariable, 1);
    var->name = g_strdup(name);
    var->value = gnc_numeric_error(GNC_ERROR_ARG);
    var->editable = TRUE;
    return var;
}
Пример #7
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);
}
Пример #8
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);
}
Пример #9
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);
}
Пример #10
0
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");

}
Пример #11
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;
}
Пример #12
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);
}
Пример #13
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;
}
Пример #14
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);
}
Пример #15
0
static void
_wipe_parsed_sx_var(gchar *key, GncSxVariable *var, gpointer unused_user_data)
{
    var->value = gnc_numeric_error(GNC_ERROR_ARG);
}
Пример #16
0
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;
}