GnuCash 2.3.0
Data Structures | Files | Typedefs
Numeric: Rational Number Handling w/ Rounding Error Control
Query Object Framework

Data Structures

struct  _gnc_numeric

Files

file  gnc-numeric.h
 

An exact-rational-number library for gnucash. (to be renamed qofnumeric.h in libqof2)


Typedefs

typedef struct _gnc_numeric gnc_numeric
 An rational-number type.

Arguments Standard Arguments to most functions

Most of the gnc_numeric arithmetic functions take two arguments in addition to their numeric args: 'denom', which is the denominator to use in the output gnc_numeric object, and 'how'. which describes how the arithmetic result is to be converted to that denominator. This combination of output denominator and rounding policy allows the results of financial and other rational computations to be properly rounded to the appropriate units.

Watch out: You must specifiy a rounding policy such as GNC_HOW_RND_NEVER, otherwise the fractional part of the input value might silently get discarded!

Valid values for denom are: GNC_DENOM_AUTO -- compute denominator exactly integer n -- Force the denominator of the result to be this integer GNC_DENOM_RECIPROCAL -- Use 1/n as the denominator (???huh???)

Valid values for 'how' are bitwise combinations of zero or one "rounding instructions" with zero or one "denominator types". Valid rounding instructions are: GNC_HOW_RND_FLOOR GNC_HOW_RND_CEIL GNC_HOW_RND_TRUNC GNC_HOW_RND_PROMOTE GNC_HOW_RND_ROUND_HALF_DOWN GNC_HOW_RND_ROUND_HALF_UP GNC_HOW_RND_ROUND GNC_HOW_RND_NEVER

The denominator type specifies how to compute a denominator if GNC_DENOM_AUTO is specified as the 'denom'. Valid denominator types are: GNC_HOW_DENOM_EXACT GNC_HOW_DENOM_REDUCE GNC_HOW_DENOM_LCD GNC_HOW_DENOM_FIXED GNC_HOW_DENOM_SIGFIGS(N)

To use traditional rational-number operational semantics (all results are exact and are reduced to relatively-prime fractions) pass the argument GNC_DENOM_AUTO as 'denom' and GNC_HOW_DENOM_REDUCE| GNC_HOW_RND_NEVER as 'how'.

To enforce strict financial semantics (such that all operands must have the same denominator as each other and as the result), use GNC_DENOM_AUTO as 'denom' and GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER as 'how'.

enum  {
  GNC_HOW_RND_FLOOR = 0x01, GNC_HOW_RND_CEIL = 0x02, GNC_HOW_RND_TRUNC = 0x03, GNC_HOW_RND_PROMOTE = 0x04,
  GNC_HOW_RND_ROUND_HALF_DOWN = 0x05, GNC_HOW_RND_ROUND_HALF_UP = 0x06, GNC_HOW_RND_ROUND = 0x07, GNC_HOW_RND_NEVER = 0x08
}
 Rounding/Truncation modes for operations. More...
enum  {
  GNC_HOW_DENOM_EXACT = 0x10, GNC_HOW_DENOM_REDUCE = 0x20, GNC_HOW_DENOM_LCD = 0x30, GNC_HOW_DENOM_FIXED = 0x40,
  GNC_HOW_DENOM_SIGFIG = 0x50
}
enum  GNCNumericErrorCode {
  GNC_ERROR_OK = 0, GNC_ERROR_ARG = -1, GNC_ERROR_OVERFLOW = -2, GNC_ERROR_DENOM_DIFF = -3,
  GNC_ERROR_REMAINDER = -4
}
#define GNC_NUMERIC_RND_MASK   0x0000000f
 bitmasks for HOW flags.
#define GNC_NUMERIC_DENOM_MASK   0x000000f0
#define GNC_NUMERIC_SIGFIGS_MASK   0x0000ff00
#define GNC_HOW_DENOM_SIGFIGS(n)   ( ((( n ) & 0xff) << 8) | GNC_HOW_DENOM_SIGFIG)
#define GNC_HOW_GET_SIGFIGS(a)   ( (( a ) & 0xff00 ) >> 8)
#define GNC_DENOM_AUTO   0
#define GNC_DENOM_RECIPROCAL(a)   (- ( a ))

Constructors

gnc_numeric double_to_gnc_numeric (double n, gint64 denom, gint how)
gboolean string_to_gnc_numeric (const gchar *str, gnc_numeric *n)
gnc_numeric gnc_numeric_error (GNCNumericErrorCode error_code)
const char * gnc_numeric_errorCode_to_string (GNCNumericErrorCode error_code)

Value Accessors

gdouble gnc_numeric_to_double (gnc_numeric n)
gchar * gnc_numeric_to_string (gnc_numeric n)
gchar * gnc_num_dbg_to_string (gnc_numeric n)

Comparisons and Predicates

GNCNumericErrorCode gnc_numeric_check (gnc_numeric a)
gint gnc_numeric_compare (gnc_numeric a, gnc_numeric b)
gboolean gnc_numeric_zero_p (gnc_numeric a)
gboolean gnc_numeric_negative_p (gnc_numeric a)
gboolean gnc_numeric_positive_p (gnc_numeric a)
gboolean gnc_numeric_eq (gnc_numeric a, gnc_numeric b)
gboolean gnc_numeric_equal (gnc_numeric a, gnc_numeric b)
gint gnc_numeric_same (gnc_numeric a, gnc_numeric b, gint64 denom, gint how)

Arithmetic Operations

gnc_numeric gnc_numeric_add (gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gnc_numeric gnc_numeric_sub (gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gnc_numeric gnc_numeric_mul (gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
gnc_numeric gnc_numeric_div (gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
gnc_numeric gnc_numeric_neg (gnc_numeric a)
gnc_numeric gnc_numeric_abs (gnc_numeric a)

Arithmetic Functions with Exact Error Returns

gnc_numeric gnc_numeric_add_with_error (gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric *error)
gnc_numeric gnc_numeric_sub_with_error (gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric *error)
gnc_numeric gnc_numeric_mul_with_error (gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric *error)
gnc_numeric gnc_numeric_div_with_error (gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric *error)

Change Denominator

gnc_numeric gnc_numeric_convert (gnc_numeric n, gint64 denom, gint how)
gnc_numeric gnc_numeric_reduce (gnc_numeric n)
gboolean gnc_numeric_to_decimal (gnc_numeric *a, guint8 *max_decimal_places)

GValue

GType gnc_numeric_get_type (void)
#define GNC_TYPE_NUMERIC   (gnc_numeric_get_type ())

Detailed Description

The 'Numeric' functions provide a way of working with rational numbers while maintaining strict control over rounding errors when adding rationals with different denominators. The Numeric class is primarily used for working with monetary amounts, where the denominator typically represents the smallest fraction of the currency (e.g. pennies, centimes). The numeric class can handle any fraction (e.g. twelfth's) and is not limited to fractions that are powers of ten.

A 'Numeric' value represents a number in rational form, with a 64-bit integer as numerator and denominator. Rationals are ideal for many uses, such as performing exact, roundoff-error-free addition and multiplication, but 64-bit rationals do not have the dynamic range of floating point numbers.

See gnc_numeric Example


Define Documentation

#define GNC_DENOM_AUTO   0

Values that can be passed as the 'denom' argument. The include a positive number n to be used as the denominator of the output value. Other possibilities include the list below: Compute an appropriate denominator automatically. Flags in the 'how' argument will specify how to compute the denominator.

Definition at line 242 of file gnc-numeric.h.

#define GNC_DENOM_RECIPROCAL (   a)    (- ( a ))

Use the value 1/n as the denominator of the output value.

Definition at line 245 of file gnc-numeric.h.

#define GNC_HOW_DENOM_SIGFIGS (   n)    ( ((( n ) & 0xff) << 8) | GNC_HOW_DENOM_SIGFIG)

Build a 'how' value that will generate a denominator that will keep at least n significant figures in the result.

Definition at line 214 of file gnc-numeric.h.

#define GNC_NUMERIC_RND_MASK   0x0000000f

bitmasks for HOW flags.

bits 8-15 of 'how' are reserved for the number of significant digits to use in the output with GNC_HOW_DENOM_SIGFIG

Definition at line 123 of file gnc-numeric.h.


Typedef Documentation

typedef struct _gnc_numeric gnc_numeric

An rational-number type.

This is a rational number, defined by numerator and denominator.

Definition at line 64 of file gnc-numeric.h.


Enumeration Type Documentation

anonymous enum

Rounding/Truncation modes for operations.

Rounding instructions control how fractional parts in the specified denominator affect the result. For example, if a computed result is "3/4" but the specified denominator for the return value is 2, should the return value be "1/2" or "2/2"?

Watch out: You must specifiy a rounding policy such as GNC_HOW_RND_NEVER, otherwise the fractional part of the input value might silently get discarded!

Possible rounding instructions are:

Enumerator:
GNC_HOW_RND_FLOOR 

Round toward -infinity

GNC_HOW_RND_CEIL 

Round toward +infinity

GNC_HOW_RND_TRUNC 

Truncate fractions (round toward zero)

GNC_HOW_RND_PROMOTE 

Promote fractions (round away from zero)

GNC_HOW_RND_ROUND_HALF_DOWN 

Round to the nearest integer, rounding toward zero when there are two equidistant nearest integers.

GNC_HOW_RND_ROUND_HALF_UP 

Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers.

GNC_HOW_RND_ROUND 

Use unbiased ("banker's") rounding. This rounds to the nearest integer, and to the nearest even integer when there are two equidistant nearest integers. This is generally the one you should use for financial quantities.

GNC_HOW_RND_NEVER 

Never round at all, and signal an error if there is a fractional result in a computation.

Definition at line 140 of file gnc-numeric.h.

anonymous enum

How to compute a denominator, or'ed into the "how" field.

Enumerator:
GNC_HOW_DENOM_EXACT 

Use any denominator which gives an exactly correct ratio of numerator to denominator. Use EXACT when you do not wish to lose any information in the result but also do not want to spend any time finding the "best" denominator.

GNC_HOW_DENOM_REDUCE 

Reduce the result value by common factor elimination, using the smallest possible value for the denominator that keeps the correct ratio. The numerator and denominator of the result are relatively prime.

GNC_HOW_DENOM_LCD 

Find the least common multiple of the arguments' denominators and use that as the denominator of the result.

GNC_HOW_DENOM_FIXED 

All arguments are required to have the same denominator, that denominator is to be used in the output, and an error is to be signaled if any argument has a different denominator.

GNC_HOW_DENOM_SIGFIG 

Round to the number of significant figures given in the rounding instructions by the GNC_HOW_DENOM_SIGFIGS () macro.

Definition at line 178 of file gnc-numeric.h.

Error codes

Enumerator:
GNC_ERROR_OK 

No error

GNC_ERROR_ARG 

Argument is not a valid number

GNC_ERROR_OVERFLOW 

Intermediate result overflow

GNC_ERROR_DENOM_DIFF 

GNC_HOW_DENOM_FIXED was specified, but argument denominators differed.

GNC_ERROR_REMAINDER 

GNC_HOW_RND_NEVER was specified, but the result could not be converted to the desired denominator without a remainder.

Definition at line 218 of file gnc-numeric.h.


Function Documentation

gnc_numeric double_to_gnc_numeric ( double  n,
gint64  denom,
gint  how 
)

Convert a floating-point number to a gnc_numeric.

Both 'denom' and 'how' are used as in arithmetic, but GNC_DENOM_AUTO is not recognized; a denominator must be specified either explicitly or through sigfigs.

See also:
Arguments
Parameters:
nThe double value that is converted into a gnc_numeric
denomThe denominator of the gnc_numeric return value. If the 'how' argument contains the GNC_HOW_DENOM_SIGFIG flag, this value will be ignored.
howDescribes the rounding policy and output denominator. Watch out: You must specifiy a rounding policy such as GNC_HOW_RND_NEVER, otherwise the fractional part of the input value is silently discarded! Common values for 'how' are (GNC_HOW_DENOM_REDUCE|GNC_HOW_RND_NEVER) or (GNC_HOW_DENOM_FIXED|GNC_HOW_RND_NEVER). As mentioned above, GNC_DENOM_AUTO is not allowed here.
Returns:
The newly created gnc_numeric rational value.

Definition at line 1160 of file gnc-numeric.c.

{
    gnc_numeric out;
    gint64 int_part = 0;
    double frac_part;
    gint64 frac_int = 0;
    double logval;
    double sigfigs;

    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 (sigfigs - logval >= 0)
        {
            denom    = (gint64)(pow(10, sigfigs - logval));
        }
        else
        {
            denom    = -((gint64)(pow(10, logval - sigfigs)));
        }

        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;
}
gchar* gnc_num_dbg_to_string ( gnc_numeric  n)

Convert to string. Uses a static, non-thread-safe buffer. For internal use only.

Definition at line 1366 of file gnc-numeric.c.

{
    static char buff[1000];
    static char *p = buff;
    gint64 tmpnum = n.num;
    gint64 tmpdenom = n.denom;

    p += 100;
    if (p - buff >= 1000) p = buff;

    sprintf(p, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, tmpdenom);

    return p;
}
gnc_numeric gnc_numeric_abs ( gnc_numeric  a)

Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value. For a given gnc_numeric "a/b" the returned value is "|a/b|".

Definition at line 749 of file gnc-numeric.c.

{
    if (gnc_numeric_check(a))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }
    return gnc_numeric_create(ABS(a.num), a.denom);
}
gnc_numeric gnc_numeric_add ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how 
)

Return a+b.

Definition at line 359 of file gnc-numeric.c.

{
    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);
}
gnc_numeric gnc_numeric_add_with_error ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how,
gnc_numeric error 
)

The same as gnc_numeric_add, but uses 'error' for accumulating conversion roundoff error.

Definition at line 1266 of file gnc-numeric.c.

{

    gnc_numeric sum   = gnc_numeric_add(a, b, denom, how);
    gnc_numeric exact = gnc_numeric_add(a, b, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    gnc_numeric err   = gnc_numeric_sub(sum, exact, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);

    if (error)
    {
        *error = err;
    }
    return sum;
}
GNCNumericErrorCode gnc_numeric_check ( gnc_numeric  a) [inline]

Check for error signal in value. Returns GNC_ERROR_OK (==0) if the number appears to be valid, otherwise it returns the type of error. Error values always have a denominator of zero.

Definition at line 64 of file gnc-numeric.c.

{
    if (G_LIKELY(in.denom != 0))
    {
        return GNC_ERROR_OK;
    }
    else if (in.num)
    {
        if ((0 < in.num) || (-4 > in.num))
        {
            in.num = (gint64) GNC_ERROR_OVERFLOW;
        }
        return (GNCNumericErrorCode) in.num;
    }
    else
    {
        return GNC_ERROR_ARG;
    }
}
gint gnc_numeric_compare ( gnc_numeric  a,
gnc_numeric  b 
)

Returns 1 if a>b, -1 if b>a, 0 if a == b

Definition at line 227 of file gnc-numeric.c.

{
    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;
}
gnc_numeric gnc_numeric_convert ( gnc_numeric  n,
gint64  denom,
gint  how 
)

Change the denominator of a gnc_numeric value to the specified denominator under standard arguments 'denom' and 'how'.

Definition at line 763 of file gnc-numeric.c.

{
    gnc_numeric out;
    gnc_numeric temp;
    gint64      temp_bc;
    gint64      temp_a;
    gint64      remainder;
    gint64      sign;
    gint        denom_neg = 0;
    double      ratio, logratio;
    double      sigfigs;
    qofint128 nume, newm;

    temp.num   = 0;
    temp.denom = 0;

    if (gnc_numeric_check(in))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    if (denom == GNC_DENOM_AUTO)
    {
        switch (how & GNC_NUMERIC_DENOM_MASK)
        {
        default:
        case GNC_HOW_DENOM_LCD:   /* LCD is meaningless with AUTO in here */
        case GNC_HOW_DENOM_EXACT:
            return in;
            break;

        case GNC_HOW_DENOM_REDUCE:
            /* reduce the input to a relatively-prime fraction */
            return gnc_numeric_reduce(in);
            break;

        case GNC_HOW_DENOM_FIXED:
            if (in.denom != denom)
            {
                return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
            }
            else
            {
                return in;
            }
            break;

        case GNC_HOW_DENOM_SIGFIG:
            ratio    = fabs(gnc_numeric_to_double(in));
            if (ratio < 10e-20)
            {
                logratio = 0;
            }
            else
            {
                logratio = log10(ratio);
                logratio = ((logratio > 0.0) ?
                            (floor(logratio) + 1.0) : (ceil(logratio)));
            }
            sigfigs  = GNC_HOW_GET_SIGFIGS(how);

            if (fabs(sigfigs - logratio) > 18)
                return gnc_numeric_error(GNC_ERROR_OVERFLOW);

            if (sigfigs - logratio >= 0)
            {
                denom    = (gint64)(pow(10, sigfigs - logratio));
            }
            else
            {
                denom    = -((gint64)(pow(10, logratio - sigfigs)));
            }

            how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK;
            break;

        }
    }

    /* Make sure we need to do the work */
    if (in.denom == denom)
    {
        return in;
    }
    if (in.num == 0)
    {
        out.num = 0;
        out.denom = denom;
        return out;
    }

    /* If the denominator of the input value is negative, get rid of that. */
    if (in.denom < 0)
    {
        in.num = in.num * (- in.denom);  /* BUG: overflow not handled.  */
        in.denom = 1;
    }

    sign = (in.num < 0) ? -1 : 1;

    /* If the denominator is less than zero, we are to interpret it as
     * the reciprocal of its magnitude. */
    if (denom < 0)
    {

        /* XXX FIXME: use 128-bit math here ... */
        denom     = - denom;
        denom_neg = 1;
        temp_a    = (in.num < 0) ? -in.num : in.num;
        temp_bc   = in.denom * denom;  /* BUG: overflow not handled.  */
        remainder = temp_a % temp_bc;
        out.num   = temp_a / temp_bc;
        out.denom = - denom;
    }
    else
    {
        /* Do all the modulo and int division on positive values to make
         * things a little clearer. Reduce the fraction denom/in.denom to
         * help with range errors */
        temp.num   = denom;
        temp.denom = in.denom;
        temp       = gnc_numeric_reduce(temp);

        /* Symbolically, do the following:
         * out.num   = in.num * temp.num;
         * remainder = out.num % temp.denom;
         * out.num   = out.num / temp.denom;
         * out.denom = denom;
         */
        nume = mult128 (in.num, temp.num);
        newm = div128 (nume, temp.denom);
        remainder = rem128 (nume, temp.denom);

        if (newm.isbig)
        {
            return gnc_numeric_error(GNC_ERROR_OVERFLOW);
        }

        out.num = newm.lo;
        out.denom = denom;
    }

    if (remainder)
    {
        switch (how & GNC_NUMERIC_RND_MASK)
        {
        case GNC_HOW_RND_FLOOR:
            if (sign < 0)
            {
                out.num = out.num + 1;
            }
            break;

        case GNC_HOW_RND_CEIL:
            if (sign > 0)
            {
                out.num = out.num + 1;
            }
            break;

        case GNC_HOW_RND_TRUNC:
            break;

        case GNC_HOW_RND_PROMOTE:
            out.num = out.num + 1;
            break;

        case GNC_HOW_RND_ROUND_HALF_DOWN:
            if (denom_neg)
            {
                if ((2 * remainder) > in.denom * denom)
                {
                    out.num = out.num + 1;
                }
            }
            else if ((2 * remainder) > temp.denom)
            {
                out.num = out.num + 1;
            }
            /* check that 2*remainder didn't over-flow */
            else if (((2 * remainder) < remainder) &&
                     (remainder > (temp.denom / 2)))
            {
                out.num = out.num + 1;
            }
            break;

        case GNC_HOW_RND_ROUND_HALF_UP:
            if (denom_neg)
            {
                if ((2 * remainder) >= in.denom * denom)
                {
                    out.num = out.num + 1;
                }
            }
            else if ((2 * remainder ) >= temp.denom)
            {
                out.num = out.num + 1;
            }
            /* check that 2*remainder didn't over-flow */
            else if (((2 * remainder) < remainder) &&
                     (remainder >= (temp.denom / 2)))
            {
                out.num = out.num + 1;
            }
            break;

        case GNC_HOW_RND_ROUND:
            if (denom_neg)
            {
                if ((2 * remainder) > in.denom * denom)
                {
                    out.num = out.num + 1;
                }
                else if ((2 * remainder) == in.denom * denom)
                {
                    if (out.num % 2)
                    {
                        out.num = out.num + 1;
                    }
                }
            }
            else
            {
                if ((2 * remainder ) > temp.denom)
                {
                    out.num = out.num + 1;
                }
                /* check that 2*remainder didn't over-flow */
                else if (((2 * remainder) < remainder) &&
                         (remainder > (temp.denom / 2)))
                {
                    out.num = out.num + 1;
                }
                else if ((2 * remainder) == temp.denom)
                {
                    if (out.num % 2)
                    {
                        out.num = out.num + 1;
                    }
                }
                /* check that 2*remainder didn't over-flow */
                else if (((2 * remainder) < remainder) &&
                         (remainder ==  (temp.denom / 2)))
                {
                    if (out.num % 2)
                    {
                        out.num = out.num + 1;
                    }
                }
            }
            break;

        case GNC_HOW_RND_NEVER:
            return gnc_numeric_error(GNC_ERROR_REMAINDER);
            break;
        }
    }

    out.num = (sign > 0) ? out.num : (-out.num);

    return out;
}
gnc_numeric gnc_numeric_div ( gnc_numeric  x,
gnc_numeric  y,
gint64  denom,
gint  how 
)

Division. Note that division can overflow, in the following sense: if we write x=a/b and y=c/d then x/y = (a*d)/(b*c) If, after eliminating all common factors between the numerator (a*d) and the denominator (b*c), then if either the numerator and/or the denominator are *still* greater than 2^63, then the division has overflowed.

Definition at line 601 of file gnc-numeric.c.

{
    gnc_numeric quotient;
    qofint128 nume, deno;

    if (gnc_numeric_check(a) || gnc_numeric_check(b))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }

    if ((denom == GNC_DENOM_AUTO) &&
            (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED)
    {
        if (a.denom == b.denom)
        {
            denom = a.denom;
        }
        else if (a.denom == 0)
        {
            denom = b.denom;
        }
        else
        {
            return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
        }
    }


    if (a.denom < 0)
    {
        a.num *= -a.denom;   /* BUG: overflow not handled.  */
        a.denom = 1;
    }

    if (b.denom < 0)
    {
        b.num *= -b.denom;   /* BUG: overflow not handled.  */
        b.denom = 1;
    }

    if (a.denom == b.denom)
    {
        quotient.num = a.num;
        quotient.denom = b.num;
    }
    else
    {
        gint64 sgn = 1;
        if (0 > a.num)
        {
            sgn = -sgn;
            a.num = -a.num;
        }
        if (0 > b.num)
        {
            sgn = -sgn;
            b.num = -b.num;
        }
        nume = mult128(a.num, b.denom);
        deno = mult128(b.num, a.denom);

        /* Try to avoid overflow by removing common factors */
        if (nume.isbig && deno.isbig)
        {
            gnc_numeric ra = gnc_numeric_reduce (a);
            gnc_numeric rb = gnc_numeric_reduce (b);

            gint64 gcf_nume = gcf64(ra.num, rb.num);
            gint64 gcf_deno = gcf64(rb.denom, ra.denom);
            nume = mult128(ra.num / gcf_nume, rb.denom / gcf_deno);
            deno = mult128(rb.num / gcf_nume, ra.denom / gcf_deno);
        }

        if ((0 == nume.isbig) && (0 == deno.isbig))
        {
            quotient.num = sgn * nume.lo;
            quotient.denom = deno.lo;
            goto dive_done;
        }
        else if (0 == deno.isbig)
        {
            quotient = reduce128 (nume, deno.lo);
            if (0 == gnc_numeric_check (quotient))
            {
                quotient.num *= sgn;
                goto dive_done;
            }
        }

        /* If rounding allowed, then shift until there's no
         * more overflow. The conversion at the end will fix
         * things up for the final value. */
        if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER)
        {
            return gnc_numeric_error (GNC_ERROR_OVERFLOW);
        }
        while (nume.isbig || deno.isbig)
        {
            nume = shift128 (nume);
            deno = shift128 (deno);
        }
        quotient.num = sgn * nume.lo;
        quotient.denom = deno.lo;
        if (0 == quotient.denom)
        {
            return gnc_numeric_error (GNC_ERROR_OVERFLOW);
        }
    }

    if (quotient.denom < 0)
    {
        quotient.num   = -quotient.num;
        quotient.denom = -quotient.denom;
    }

dive_done:
    if ((denom == GNC_DENOM_AUTO) &&
            ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD))
    {
        denom = gnc_numeric_lcd(a, b);
        how   = how & GNC_NUMERIC_RND_MASK;
    }

    return gnc_numeric_convert(quotient, denom, how);
}
gnc_numeric gnc_numeric_div_with_error ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how,
gnc_numeric error 
)

The same as gnc_numeric_div, but uses error for accumulating conversion roundoff error.

Definition at line 1333 of file gnc-numeric.c.

{
    gnc_numeric quot  = gnc_numeric_div(a, b, denom, how);
    gnc_numeric exact = gnc_numeric_div(a, b, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    gnc_numeric err   = gnc_numeric_sub(quot, exact,
                                        GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
    if (error)
    {
        *error = err;
    }
    return quot;
}
gboolean gnc_numeric_eq ( gnc_numeric  a,
gnc_numeric  b 
)

Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and denominator)

Definition at line 274 of file gnc-numeric.c.

{
    return ((a.num == b.num) && (a.denom == b.denom));
}
gboolean gnc_numeric_equal ( gnc_numeric  a,
gnc_numeric  b 
)

Equivalence predicate: Returns TRUE (1) if a and b represent the same number. That is, return TRUE if the ratios, when reduced by eliminating common factors, are identical.

Definition at line 285 of file gnc-numeric.c.

{
    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));
}
gnc_numeric gnc_numeric_error ( GNCNumericErrorCode  error_code)

Create a gnc_numeric object that signals the error condition noted by error_code, rather than a number.

Definition at line 1255 of file gnc-numeric.c.

{
    return gnc_numeric_create(error_code, 0LL);
}
const char* gnc_numeric_errorCode_to_string ( GNCNumericErrorCode  error_code)

Returns a string representation of the given GNCNumericErrorCode.

Definition at line 1515 of file gnc-numeric.c.

{
    switch (error_code)
    {
    case GNC_ERROR_OK:
        return "GNC_ERROR_OK";
    case GNC_ERROR_ARG:
        return "GNC_ERROR_ARG";
    case GNC_ERROR_OVERFLOW:
        return "GNC_ERROR_OVERFLOW";
    case GNC_ERROR_DENOM_DIFF:
        return "GNC_ERROR_DENOM_DIFF";
    case GNC_ERROR_REMAINDER:
        return "GNC_ERROR_REMAINDER";
    default:
        return "<unknown>";
    }
}
gnc_numeric gnc_numeric_mul ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how 
)

Multiply a times b, returning the product. An overflow may occur if the result of the multiplication can't be represented as a ratio of 64-bit int's after removing common factors.

Definition at line 473 of file gnc-numeric.c.

{
    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);
            }
        }
    }

#if 0  /* currently, product denom won't ever be zero */
    if (product.denom < 0)
    {
        product.num   = -product.num;
        product.denom = -product.denom;
    }
#endif

    result = gnc_numeric_convert(product, denom, how);
    return result;
}
gnc_numeric gnc_numeric_mul_with_error ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how,
gnc_numeric error 
)

The same as gnc_numeric_mul, but uses error for accumulating conversion roundoff error.

Definition at line 1311 of file gnc-numeric.c.

{
    gnc_numeric prod  = gnc_numeric_mul(a, b, denom, how);
    gnc_numeric exact = gnc_numeric_mul(a, b, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    gnc_numeric err   = gnc_numeric_sub(prod, exact, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    if (error)
    {
        *error = err;
    }
    return prod;
}
gnc_numeric gnc_numeric_neg ( gnc_numeric  a)

Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value. For a given gnc_numeric "a/b" the returned value is "-a/b".

Definition at line 734 of file gnc-numeric.c.

{
    if (gnc_numeric_check(a))
    {
        return gnc_numeric_error(GNC_ERROR_ARG);
    }
    return gnc_numeric_create(- a.num, a.denom);
}
gboolean gnc_numeric_negative_p ( gnc_numeric  a)

Returns 1 if a < 0, otherwise returns 0.

Definition at line 178 of file gnc-numeric.c.

{
    if (gnc_numeric_check(a))
    {
        return 0;
    }
    else
    {
        if ((a.num < 0) && (a.denom != 0))
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}
gboolean gnc_numeric_positive_p ( gnc_numeric  a)

Returns 1 if a > 0, otherwise returns 0.

Definition at line 202 of file gnc-numeric.c.

{
    if (gnc_numeric_check(a))
    {
        return 0;
    }
    else
    {
        if ((a.num > 0) && (a.denom != 0))
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}
gnc_numeric gnc_numeric_reduce ( gnc_numeric  n)

Return input after reducing it by Greated Common Factor (GCF) elimination

Definition at line 1035 of file gnc-numeric.c.

{
    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;
}
gint gnc_numeric_same ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how 
)

Equivalence predicate: Convert both a and b to denom using the specified DENOM and method HOW, and compare numerators the results using gnc_numeric_equal.

For example, if a == 7/16 and b == 3/4, gnc_numeric_same(a, b, 2, GNC_HOW_RND_TRUNC) == 1 because both 7/16 and 3/4 round to 1/2 under truncation. However, gnc_numeric_same(a, b, 2, GNC_HOW_RND_ROUND) == 0 because 7/16 rounds to 1/2 under unbiased rounding but 3/4 rounds to 2/2.

Definition at line 341 of file gnc-numeric.c.

{
    gnc_numeric aconv, bconv;

    aconv = gnc_numeric_convert(a, denom, how);
    bconv = gnc_numeric_convert(b, denom, how);

    return(gnc_numeric_equal(aconv, bconv));
}
gnc_numeric gnc_numeric_sub ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how 
)

Return a-b.

Definition at line 454 of file gnc-numeric.c.

{
    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);
}
gnc_numeric gnc_numeric_sub_with_error ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how,
gnc_numeric error 
)

The same as gnc_numeric_sub, but uses error for accumulating conversion roundoff error.

Definition at line 1289 of file gnc-numeric.c.

{
    gnc_numeric diff  = gnc_numeric_sub(a, b, denom, how);
    gnc_numeric exact = gnc_numeric_sub(a, b, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    gnc_numeric err   = gnc_numeric_sub(diff, exact, GNC_DENOM_AUTO,
                                        GNC_HOW_DENOM_REDUCE);
    if (error)
    {
        *error = err;
    }
    return diff;
}
gboolean gnc_numeric_to_decimal ( gnc_numeric a,
guint8 *  max_decimal_places 
)

Attempt to convert the denominator to an exact power of ten without rounding.

Parameters:
athe gnc_numeric value to convert
max_decimal_placesthe number of decimal places of the converted value. This parameter may be NULL.
Returns:
TRUE if a has been converted or was already decimal. Otherwise, FALSE is returned and a and max_decimal_places remain unchanged.

Definition at line 1074 of file gnc-numeric.c.

{
    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;
}
gdouble gnc_numeric_to_double ( gnc_numeric  n)

Convert numeric to floating-point value.

Definition at line 1238 of file gnc-numeric.c.

{
    if (in.denom > 0)
    {
        return (double)in.num / (double)in.denom;
    }
    else
    {
        return (double)(in.num * -in.denom);
    }
}
gchar* gnc_numeric_to_string ( gnc_numeric  n)

Convert to string. The returned buffer is to be g_free'd by the caller (it was allocated through g_strdup)

Definition at line 1354 of file gnc-numeric.c.

{
    gchar *result;
    gint64 tmpnum = n.num;
    gint64 tmpdenom = n.denom;

    result = g_strdup_printf("%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, tmpdenom);

    return result;
}
gboolean gnc_numeric_zero_p ( gnc_numeric  a)

Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.

Definition at line 154 of file gnc-numeric.c.

{
    if (gnc_numeric_check(a))
    {
        return 0;
    }
    else
    {
        if ((a.num == 0) && (a.denom != 0))
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
}
gboolean string_to_gnc_numeric ( const gchar *  str,
gnc_numeric n 
)

Read a gnc_numeric from str, skipping any leading whitespace. Return TRUE on success and store the resulting value in "n". Return NULL on error.

Definition at line 1382 of file gnc-numeric.c.

{
    size_t num_read;
    gint64 tmpnum;
    gint64 tmpdenom;

    if (!str) return FALSE;

#ifdef GNC_DEPRECATED
    /* must use "<" here because %n's effects aren't well defined */
    if (sscanf(str, " " QOF_SCANF_LLD "/" QOF_SCANF_LLD "%n",
               &tmpnum, &tmpdenom, &num_read) < 2)
    {
        return FALSE;
    }
#else
    tmpnum = g_ascii_strtoll (str, NULL, 0);
    str = strchr (str, '/');
    if (!str) return FALSE;
    str ++;
    tmpdenom = g_ascii_strtoll (str, NULL, 0);
    num_read = strspn (str, "0123456789");
#endif
    n->num = tmpnum;
    n->denom = tmpdenom;
    return TRUE;
}
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines