示例#1
0
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));
}
示例#2
0
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);
}
示例#3
0
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)");
}
示例#4
0
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);
    }
}
示例#5
0
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");

        }
    }
}
示例#6
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;
}
示例#7
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);
}
示例#8
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;
}
示例#9
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;
}