QOF 0.7.5
Numeric: Rational Number Handling with Rounding Error Control

Data Structures

struct  _QofNumeric

Modules

 Math128

Files

file  qofnumeric.h
 

An exact-rational-number library for QOF.


Typedefs

typedef struct _QofNumeric QofNumeric
 A rational-number type.

Standard Arguments to most functions

Most of the QofNumeric arithmetic functions take two arguments in addition to their numeric args: 'denom', which is the denominator to use in the output QofNumeric 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.

Valid values for denom are: QOF_DENOM_AUTO -- compute denominator exactly integer n -- Force the denominator of the result to be this integer QOF_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: QOF_HOW_RND_FLOOR QOF_HOW_RND_CEIL QOF_HOW_RND_TRUNC QOF_HOW_RND_PROMOTE QOF_HOW_RND_ROUND_HALF_DOWN QOF_HOW_RND_ROUND_HALF_UP QOF_HOW_RND_ROUND QOF_HOW_RND_NEVER

The denominator type specifies how to compute a denominator if QOF_DENOM_AUTO is specified as the 'denom'. Valid denominator types are: QOF_HOW_DENOM_EXACT QOF_HOW_DENOM_REDUCE QOF_HOW_DENOM_LCD QOF_HOW_DENOM_FIXED QOF_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 QOF_DENOM_AUTO as 'denom' and QOF_HOW_DENOM_REDUCE| QOF_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 QOF_DENOM_AUTO as 'denom' and QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER as 'how'.

enum  {
  QOF_HOW_RND_FLOOR = 0x01, QOF_HOW_RND_CEIL = 0x02, QOF_HOW_RND_TRUNC = 0x03, QOF_HOW_RND_PROMOTE = 0x04,
  QOF_HOW_RND_ROUND_HALF_DOWN = 0x05, QOF_HOW_RND_ROUND_HALF_UP = 0x06, QOF_HOW_RND_ROUND = 0x07, QOF_HOW_RND_NEVER = 0x08
}
 Rounding/Truncation modes for operations. More...
enum  {
  QOF_HOW_DENOM_EXACT = 0x10, QOF_HOW_DENOM_REDUCE = 0x20, QOF_HOW_DENOM_LCD = 0x30, QOF_HOW_DENOM_FIXED = 0x40,
  QOF_HOW_DENOM_SIGFIG = 0x50
}
enum  QofNumericErrorCode {
  QOF_ERROR_OK = 0, QOF_ERROR_ARG = -1, QOF_ERROR_OVERFLOW = -2, QOF_ERROR_DENOM_DIFF = -3,
  QOF_ERROR_REMAINDER = -4
}
#define QOF_NUMERIC_RND_MASK   0x0000000f
 bitmasks for HOW flags.
#define QOF_NUMERIC_DENOM_MASK   0x000000f0
#define QOF_NUMERIC_SIGFIGS_MASK   0x0000ff00
#define QOF_HOW_DENOM_SIGFIGS(n)   ( ((( n ) & 0xff) << 8) | QOF_HOW_DENOM_SIGFIG)
#define QOF_HOW_GET_SIGFIGS(a)   ( (( a ) & 0xff00 ) >> 8)
#define QOF_DENOM_AUTO   0
#define QOF_DENOM_RECIPROCAL(a)   (- ( a ))

Constructors

static QofNumeric qof_numeric_create (gint64 num, gint64 denom)
static QofNumeric qof_numeric_zero (void)
QofNumeric qof_numeric_from_double (gdouble in, gint64 denom, gint how)
gboolean qof_numeric_from_string (const gchar *str, QofNumeric *n)
QofNumeric qof_numeric_error (QofNumericErrorCode error_code)

Value Accessors

static gint64 qof_numeric_num (QofNumeric a)
static gint64 qof_numeric_denom (QofNumeric a)
gdouble qof_numeric_to_double (QofNumeric in)
gchar * qof_numeric_to_string (QofNumeric n)
gchar * qof_numeric_dbg_to_string (QofNumeric n)

Comparisons and Predicates

QofNumericErrorCode qof_numeric_check (QofNumeric a)
gint qof_numeric_compare (QofNumeric a, QofNumeric b)
gboolean qof_numeric_zero_p (QofNumeric a)
gboolean qof_numeric_negative_p (QofNumeric a)
gboolean qof_numeric_positive_p (QofNumeric a)
gboolean qof_numeric_eq (QofNumeric a, QofNumeric b)
gboolean qof_numeric_equal (QofNumeric a, QofNumeric b)
gint qof_numeric_same (QofNumeric a, QofNumeric b, gint64 denom, gint how)

Arithmetic Operations

QofNumeric qof_numeric_add (QofNumeric a, QofNumeric b, gint64 denom, gint how)
QofNumeric qof_numeric_sub (QofNumeric a, QofNumeric b, gint64 denom, gint how)
QofNumeric qof_numeric_mul (QofNumeric a, QofNumeric b, gint64 denom, gint how)
QofNumeric qof_numeric_div (QofNumeric x, QofNumeric y, gint64 denom, gint how)
QofNumeric qof_numeric_neg (QofNumeric a)
QofNumeric qof_numeric_abs (QofNumeric a)
static QofNumeric qof_numeric_add_fixed (QofNumeric a, QofNumeric b)
static QofNumeric qof_numeric_sub_fixed (QofNumeric a, QofNumeric b)

Arithmetic Functions with Exact Error Returns

QofNumeric qof_numeric_add_with_error (QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
QofNumeric qof_numeric_sub_with_error (QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
QofNumeric qof_numeric_mul_with_error (QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)
QofNumeric qof_numeric_div_with_error (QofNumeric a, QofNumeric b, gint64 denom, gint how, QofNumeric *error)

Change Denominator

QofNumeric qof_numeric_convert (QofNumeric in, gint64 denom, gint how)
QofNumeric qof_numeric_convert_with_error (QofNumeric in, gint64 denom, gint how, QofNumeric *error)
QofNumeric qof_numeric_reduce (QofNumeric in)

Deprecated, backwards-compatible definitions

#define QOF_RND_FLOOR   QOF_HOW_RND_FLOOR
#define QOF_RND_CEIL   QOF_HOW_RND_CEIL
#define QOF_RND_TRUNC   QOF_HOW_RND_TRUNC
#define QOF_RND_PROMOTE   QOF_HOW_RND_PROMOTE
#define QOF_RND_ROUND_HALF_DOWN   QOF_HOW_RND_ROUND_HALF_DOWN
#define QOF_RND_ROUND_HALF_UP   QOF_HOW_RND_ROUND_HALF_UP
#define QOF_RND_ROUND   QOF_HOW_RND_ROUND
#define QOF_RND_NEVER   QOF_HOW_RND_NEVER
#define QOF_DENOM_EXACT   QOF_HOW_DENOM_EXACT
#define QOF_DENOM_REDUCE   QOF_HOW_DENOM_REDUCE
#define QOF_DENOM_LCD   QOF_HOW_DENOM_LCD
#define QOF_DENOM_FIXED   QOF_HOW_DENOM_FIXED
#define QOF_DENOM_SIGFIG   QOF_HOW_DENOM_SIGFIG
#define QOF_DENOM_SIGFIGS(X)   QOF_HOW_DENOM_SIGFIGS(X)
#define QOF_NUMERIC_GET_SIGFIGS(X)   QOF_HOW_GET_SIGFIGS(X)

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 QofNumeric Example


Define Documentation

#define QOF_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 231 of file qofnumeric.h.

#define QOF_DENOM_RECIPROCAL (   a)    (- ( a ))

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

Definition at line 234 of file qofnumeric.h.

#define QOF_HOW_DENOM_SIGFIGS (   n)    ( ((( n ) & 0xff) << 8) | QOF_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 203 of file qofnumeric.h.

#define QOF_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 QOF_HOW_DENOM_SIGFIG

Definition at line 116 of file qofnumeric.h.


Typedef Documentation

typedef struct _QofNumeric QofNumeric

A rational-number type.

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

Definition at line 61 of file qofnumeric.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"?

Possible rounding instructions are:

Enumerator:
QOF_HOW_RND_FLOOR 

Round toward -infinity

QOF_HOW_RND_CEIL 

Round toward +infinity

QOF_HOW_RND_TRUNC 

Truncate fractions (round toward zero)

QOF_HOW_RND_PROMOTE 

Promote fractions (round away from zero)

QOF_HOW_RND_ROUND_HALF_DOWN 

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

QOF_HOW_RND_ROUND_HALF_UP 

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

QOF_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.

QOF_HOW_RND_NEVER 

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

Definition at line 129 of file qofnumeric.h.

anonymous enum

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

Enumerator:
QOF_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.

QOF_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.

QOF_HOW_DENOM_LCD 

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

QOF_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.

QOF_HOW_DENOM_SIGFIG 

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

Definition at line 167 of file qofnumeric.h.

Error codes

Enumerator:
QOF_ERROR_OK 

No error

QOF_ERROR_ARG 

Argument is not a valid number

QOF_ERROR_OVERFLOW 

Intermediate result overflow

QOF_ERROR_DENOM_DIFF 

QOF_HOW_DENOM_FIXED was specified, but argument denominators differed.

QOF_ERROR_REMAINDER 

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

Definition at line 207 of file qofnumeric.h.


Function Documentation

QofNumeric qof_numeric_abs ( QofNumeric  a)

Return the absolute value of the argument

Definition at line 650 of file qofnumeric.c.

{
    if (qof_numeric_check (a))
        return qof_numeric_error (QOF_ERROR_ARG);
    return qof_numeric_create (ABS (a.num), a.denom);
}
QofNumeric qof_numeric_add ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how 
)

Return a+b.

Definition at line 300 of file qofnumeric.c.

{
    QofNumeric sum;

    if (qof_numeric_check (a) || qof_numeric_check (b))
        return qof_numeric_error (QOF_ERROR_ARG);

    if ((denom == QOF_DENOM_AUTO) &&
        (how & QOF_NUMERIC_DENOM_MASK) == QOF_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 qof_numeric_error (QOF_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 = qof_numeric_lcd (a, b);
        if (QOF_ERROR_ARG == lcd)
            return qof_numeric_error (QOF_ERROR_OVERFLOW);
        ca = mult128 (a.num, lcd / a.denom);
        if (ca.isbig)
            return qof_numeric_error (QOF_ERROR_OVERFLOW);
        cb = mult128 (b.num, lcd / b.denom);
        if (cb.isbig)
            return qof_numeric_error (QOF_ERROR_OVERFLOW);
        cab = add128 (ca, cb);
        if (cab.isbig)
            return qof_numeric_error (QOF_ERROR_OVERFLOW);
        sum.num = cab.lo;
        if (cab.isneg)
            sum.num = -sum.num;
        sum.denom = lcd;
    }

    if ((denom == QOF_DENOM_AUTO) &&
        ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
    {
        denom = qof_numeric_lcd (a, b);
        how = how & QOF_NUMERIC_RND_MASK;
    }

    return qof_numeric_convert (sum, denom, how);
}
static QofNumeric qof_numeric_add_fixed ( QofNumeric  a,
QofNumeric  b 
) [inline, static]

Shortcut for common case: QofNumeric_add(a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER);

Definition at line 411 of file qofnumeric.h.

QofNumeric qof_numeric_add_with_error ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how,
QofNumeric error 
)

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

Definition at line 1014 of file qofnumeric.c.

{

    QofNumeric sum = qof_numeric_add (a, b, denom, how);
    QofNumeric exact = qof_numeric_add (a, b, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    QofNumeric err = qof_numeric_sub (sum, exact, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);

    if (error)
        *error = err;
    return sum;
}
QofNumericErrorCode qof_numeric_check ( QofNumeric  a) [inline]

Check for error signal in value. Returns QOF_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 44 of file qofnumeric.c.

{
    if (in.denom != 0)
        return QOF_ERROR_OK;
    else if (in.num)
    {
        if ((0 < in.num) || (-4 > in.num))
            in.num = (gint64) QOF_ERROR_OVERFLOW;
        return (QofNumericErrorCode) in.num;
    }
    else
        return QOF_ERROR_ARG;
}
gint qof_numeric_compare ( QofNumeric  a,
QofNumeric  b 
)

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

Definition at line 174 of file qofnumeric.c.

{
    gint64 aa, bb;
    QofInt128 l, r;

    if (qof_numeric_check (a) || qof_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;
}
QofNumeric qof_numeric_convert ( QofNumeric  in,
gint64  denom,
gint  how 
)

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

Definition at line 662 of file qofnumeric.c.

{
    QofNumeric out;
    QofNumeric temp;
    gint64 temp_bc;
    gint64 temp_a;
    gint64 remainder;
    gint64 sign;
    gint denom_neg = 0;
    gdouble ratio, logratio;
    gdouble sigfigs;
    QofInt128 nume, newm;

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

    if (qof_numeric_check (in))
        return qof_numeric_error (QOF_ERROR_ARG);

    if (denom == QOF_DENOM_AUTO)
    {
        switch (how & QOF_NUMERIC_DENOM_MASK)
        {
        default:
        case QOF_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */
        case QOF_HOW_DENOM_EXACT:
            return in;
            break;

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

        case QOF_HOW_DENOM_FIXED:
            if (in.denom != denom)
                return qof_numeric_error (QOF_ERROR_DENOM_DIFF);
            else
                return in;
            break;

        case QOF_HOW_DENOM_SIGFIG:
            ratio = fabs (qof_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 = QOF_HOW_GET_SIGFIGS (how);

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

            how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_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 = qof_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 qof_numeric_error (QOF_ERROR_OVERFLOW);

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

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

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

        case QOF_HOW_RND_TRUNC:
            break;

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

        case QOF_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 QOF_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 QOF_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 QOF_HOW_RND_NEVER:
            return qof_numeric_error (QOF_ERROR_REMAINDER);
            break;
        }
    }

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

    return out;
}
QofNumeric qof_numeric_convert_with_error ( QofNumeric  in,
gint64  denom,
gint  how,
QofNumeric error 
)

Same as QofNumeric_convert, but return a remainder value for accumulating conversion error.

static QofNumeric qof_numeric_create ( gint64  num,
gint64  denom 
) [inline, static]

Make a QofNumeric from numerator and denominator

Definition at line 243 of file qofnumeric.h.

{
    QofNumeric out;
    out.num = num;
    out.denom = denom;
    return out;
}
gchar* qof_numeric_dbg_to_string ( QofNumeric  n)

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

Definition at line 1103 of file qofnumeric.c.

{
    static gchar buff[1000];
    static gchar *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;
}
static gint64 qof_numeric_denom ( QofNumeric  a) [inline, static]

Return denominator

Definition at line 291 of file qofnumeric.h.

{
    return a.denom;
}
QofNumeric qof_numeric_div ( QofNumeric  x,
QofNumeric  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 516 of file qofnumeric.c.

{
    QofNumeric quotient;
    QofInt128 nume, deno;

    if (qof_numeric_check (a) || qof_numeric_check (b))
        return qof_numeric_error (QOF_ERROR_ARG);

    if ((denom == QOF_DENOM_AUTO) &&
        (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED)
    {
        if (a.denom == b.denom)
            denom = a.denom;
        else if (a.denom == 0)
            denom = b.denom;
        else
            return qof_numeric_error (QOF_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)
        {
            QofNumeric ra = qof_numeric_reduce (a);
            QofNumeric rb = qof_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 == qof_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 & QOF_NUMERIC_RND_MASK) == QOF_HOW_RND_NEVER)
            return qof_numeric_error (QOF_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 qof_numeric_error (QOF_ERROR_OVERFLOW);
        }
    }

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

  dive_done:
    if ((denom == QOF_DENOM_AUTO) &&
        ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
    {
        denom = qof_numeric_lcd (a, b);
        how = how & QOF_NUMERIC_RND_MASK;
    }

    return qof_numeric_convert (quotient, denom, how);
}
QofNumeric qof_numeric_div_with_error ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how,
QofNumeric error 
)

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

Definition at line 1071 of file qofnumeric.c.

{
    QofNumeric quot = qof_numeric_div (a, b, denom, how);
    QofNumeric exact = qof_numeric_div (a, b, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    QofNumeric err = qof_numeric_sub (quot, exact,
        QOF_DENOM_AUTO, QOF_HOW_DENOM_REDUCE);
    if (error)
        *error = err;
    return quot;
}
gboolean qof_numeric_eq ( QofNumeric  a,
QofNumeric  b 
)

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

Definition at line 223 of file qofnumeric.c.

{
    return ((a.num == b.num) && (a.denom == b.denom));
}
gboolean qof_numeric_equal ( QofNumeric  a,
QofNumeric  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 233 of file qofnumeric.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
        QofNumeric ra = QofNumeric_reduce (a);
        QofNumeric rb = QofNumeric_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 funtion
           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));
}
QofNumeric qof_numeric_error ( QofNumericErrorCode  error_code)

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

Definition at line 1004 of file qofnumeric.c.

{
    return qof_numeric_create (error_code, 0LL);
}
QofNumeric qof_numeric_from_double ( gdouble  in,
gint64  denom,
gint  how 
)

Convert a floating-point number to a QofNumeric. Both 'denom' and 'how' are used as in arithmetic, but QOF_DENOM_AUTO is not recognized; a denominator must be specified either explicitctly or through sigfigs.

Definition at line 919 of file qofnumeric.c.

{
    QofNumeric out;
    gint64 int_part = 0;
    gdouble frac_part;
    gint64 frac_int = 0;
    gdouble logval;
    gdouble sigfigs;

    if ((denom == QOF_DENOM_AUTO) && (how & QOF_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 = QOF_HOW_GET_SIGFIGS (how);
        if (sigfigs - logval >= 0)
            denom = (gint64) (pow (10, sigfigs - logval));
        else
            denom = -((gint64) (pow (10, logval - sigfigs)));

        how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_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 & QOF_NUMERIC_RND_MASK)
    {
    case QOF_HOW_RND_FLOOR:
        frac_int = (gint64) floor (frac_part);
        break;

    case QOF_HOW_RND_CEIL:
        frac_int = (gint64) ceil (frac_part);
        break;

    case QOF_HOW_RND_TRUNC:
        frac_int = (gint64) frac_part;
        break;

    case QOF_HOW_RND_ROUND:
    case QOF_HOW_RND_ROUND_HALF_UP:
        frac_int = (gint64) rint (frac_part);
        break;

    case QOF_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;
}
gboolean qof_numeric_from_string ( const gchar *  str,
QofNumeric n 
)

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

Definition at line 1121 of file qofnumeric.c.

{
    size_t num_read;
    gint64 tmpnum;
    gint64 tmpdenom;

    if (!str)
        return FALSE;

#ifdef QOF_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 = strtoll (str, NULL, 0);
    str = strchr (str, '/');
    if (!str)
        return FALSE;
    str++;
    tmpdenom = strtoll (str, NULL, 0);
    num_read = strspn (str, "0123456789");
#endif
    n->num = tmpnum;
    n->denom = tmpdenom;
    return TRUE;
}
QofNumeric qof_numeric_mul ( QofNumeric  a,
QofNumeric  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 405 of file qofnumeric.c.

{
    QofNumeric product, result;
    QofInt128 bignume, bigdeno;

    if (qof_numeric_check (a) || qof_numeric_check (b))
        return qof_numeric_error (QOF_ERROR_ARG);

    if ((denom == QOF_DENOM_AUTO) &&
        (how & QOF_NUMERIC_DENOM_MASK) == QOF_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 qof_numeric_error (QOF_ERROR_DENOM_DIFF);
    }

    if ((denom == QOF_DENOM_AUTO) &&
        ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
    {
        denom = qof_numeric_lcd (a, b);
        how = how & QOF_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 = qof_numeric_reduce (a);
        b = qof_numeric_reduce (b);
        tmp = a.num;
        a.num = b.num;
        b.num = tmp;
        a = qof_numeric_reduce (a);
        b = qof_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 & QOF_NUMERIC_RND_MASK) == QOF_HOW_RND_NEVER)
        {
            if (bigdeno.isbig)
                return qof_numeric_error (QOF_ERROR_OVERFLOW);
            product = reduce128 (bignume, product.denom);
            if (qof_numeric_check (product))
                return qof_numeric_error (QOF_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 qof_numeric_error (QOF_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 = qof_numeric_convert (product, denom, how);
    return result;
}
QofNumeric qof_numeric_mul_with_error ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how,
QofNumeric error 
)

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

Definition at line 1052 of file qofnumeric.c.

{
    QofNumeric prod = qof_numeric_mul (a, b, denom, how);
    QofNumeric exact = qof_numeric_mul (a, b, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    QofNumeric err = qof_numeric_sub (prod, exact, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    if (error)
        *error = err;
    return prod;
}
QofNumeric qof_numeric_neg ( QofNumeric  a)

Negate the argument

Definition at line 637 of file qofnumeric.c.

{
    if (qof_numeric_check (a))
        return qof_numeric_error (QOF_ERROR_ARG);
    return qof_numeric_create (-a.num, a.denom);
}
gboolean qof_numeric_negative_p ( QofNumeric  a)

Returns 1 if a < 0, otherwise returns 0.

Definition at line 137 of file qofnumeric.c.

{
    if (qof_numeric_check (a))
        return 0;
    else
    {
        if ((a.num < 0) && (a.denom != 0))
            return 1;
        else
            return 0;
        }
}
static gint64 qof_numeric_num ( QofNumeric  a) [inline, static]

Return numerator

Definition at line 284 of file qofnumeric.h.

{
    return a.num;
}
gboolean qof_numeric_positive_p ( QofNumeric  a)

Returns 1 if a > 0, otherwise returns 0.

Definition at line 155 of file qofnumeric.c.

{
    if (qof_numeric_check (a))
        return 0;
    else
    {
        if ((a.num > 0) && (a.denom != 0))
            return 1;
        else
            return 0;
        }
}
QofNumeric qof_numeric_reduce ( QofNumeric  in)

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

Definition at line 888 of file qofnumeric.c.

{
    gint64 t;
    gint64 num = (in.num < 0) ? (-in.num) : in.num;
    gint64 denom = in.denom;
    QofNumeric out;

    if (qof_numeric_check (in))
        return qof_numeric_error (QOF_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 qof_numeric_same ( QofNumeric  a,
QofNumeric  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 QofNumeric_equal.

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

Definition at line 285 of file qofnumeric.c.

{
    QofNumeric aconv, bconv;

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

    return (qof_numeric_equal (aconv, bconv));
}
QofNumeric qof_numeric_sub ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how 
)

Return a-b.

Definition at line 388 of file qofnumeric.c.

{
    QofNumeric nb;

    if (qof_numeric_check (a) || qof_numeric_check (b))
        return qof_numeric_error (QOF_ERROR_ARG);

    nb = b;
    nb.num = -nb.num;
    return qof_numeric_add (a, nb, denom, how);
}
static QofNumeric qof_numeric_sub_fixed ( QofNumeric  a,
QofNumeric  b 
) [inline, static]

Shortcut for most common case: QofNumeric_sub(a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER);

Definition at line 422 of file qofnumeric.h.

QofNumeric qof_numeric_sub_with_error ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how,
QofNumeric error 
)

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

Definition at line 1034 of file qofnumeric.c.

{
    QofNumeric diff = qof_numeric_sub (a, b, denom, how);
    QofNumeric exact = qof_numeric_sub (a, b, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    QofNumeric err = qof_numeric_sub (diff, exact, QOF_DENOM_AUTO,
        QOF_HOW_DENOM_REDUCE);
    if (error)
        *error = err;
    return diff;
}
gdouble qof_numeric_to_double ( QofNumeric  in)

Convert numeric to floating-point value.

Definition at line 991 of file qofnumeric.c.

{
    if (in.denom > 0)
        return (gdouble) in.num / (gdouble) in.denom;
    else
        return (gdouble) (in.num * -in.denom);
}
gchar* qof_numeric_to_string ( QofNumeric  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 1089 of file qofnumeric.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;
}
static QofNumeric qof_numeric_zero ( void  ) [inline, static]

create a zero-value QofNumeric

Definition at line 253 of file qofnumeric.h.

{
    return qof_numeric_create (0, 1);
}
gboolean qof_numeric_zero_p ( QofNumeric  a)

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

Definition at line 119 of file qofnumeric.c.

{
    if (qof_numeric_check (a))
        return 0;
    else
    {
        if ((a.num == 0) && (a.denom != 0))
            return 1;
        else
            return 0;
        }
}