QOF  0.7.5
Numeric: Rational Number Handling with Rounding Error Control

Modules

 Math128
 

Files

file  qofnumeric.h
 An exact-rational-number library for QOF.
 

Data Structures

struct  _QofNumeric
 

Typedefs

typedef struct _QofNumeric QofNumeric
 A rational-number type. More...
 

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. More...
 
#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

Macro Definition 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.

130 {
132  QOF_HOW_RND_FLOOR = 0x01,
133 
135  QOF_HOW_RND_CEIL = 0x02,
136 
138  QOF_HOW_RND_TRUNC = 0x03,
139 
141  QOF_HOW_RND_PROMOTE = 0x04,
142 
147 
152 
158  QOF_HOW_RND_ROUND = 0x07,
159 
163  QOF_HOW_RND_NEVER = 0x08
164 };
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.

168 {
174  QOF_HOW_DENOM_EXACT = 0x10,
175 
181  QOF_HOW_DENOM_REDUCE = 0x20,
182 
186  QOF_HOW_DENOM_LCD = 0x30,
187 
192  QOF_HOW_DENOM_FIXED = 0x40,
193 
197  QOF_HOW_DENOM_SIGFIG = 0x50
198 };

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.

208 {
209  QOF_ERROR_OK = 0,
210  QOF_ERROR_ARG = -1,
211  QOF_ERROR_OVERFLOW = -2,
215 

Function Documentation

QofNumeric qof_numeric_abs ( QofNumeric  a)

Return the absolute value of the argument

Definition at line 650 of file qofnumeric.c.

651 {
652  if (qof_numeric_check (a))
654  return qof_numeric_create (ABS (a.num), a.denom);
655 }
QofNumeric qof_numeric_add ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how 
)

Return a+b.

Definition at line 300 of file qofnumeric.c.

301 {
302  QofNumeric sum;
303 
304  if (qof_numeric_check (a) || qof_numeric_check (b))
306 
307  if ((denom == QOF_DENOM_AUTO) &&
308  (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED)
309  {
310  if (a.denom == b.denom)
311  denom = a.denom;
312  else if (b.num == 0)
313  {
314  denom = a.denom;
315  b.denom = a.denom;
316  }
317  else if (a.num == 0)
318  {
319  denom = b.denom;
320  a.denom = b.denom;
321  }
322  else
324  }
325 
326  if (a.denom < 0)
327  {
328  a.num *= -a.denom; /* BUG: overflow not handled. */
329  a.denom = 1;
330  }
331 
332  if (b.denom < 0)
333  {
334  b.num *= -b.denom; /* BUG: overflow not handled. */
335  b.denom = 1;
336  }
337 
338  /* Get an exact answer.. same denominator is the common case. */
339  if (a.denom == b.denom)
340  {
341  sum.num = a.num + b.num; /* BUG: overflow not handled. */
342  sum.denom = a.denom;
343  }
344  else
345  {
346  /* We want to do this:
347  * sum.num = a.num*b.denom + b.num*a.denom;
348  * sum.denom = a.denom*b.denom;
349  * but the multiply could overflow.
350  * Computing the LCD minimizes likelihood of overflow
351  */
352  gint64 lcd;
353  QofInt128 ca, cb, cab;
354 
355  lcd = qof_numeric_lcd (a, b);
356  if (QOF_ERROR_ARG == lcd)
358  ca = mult128 (a.num, lcd / a.denom);
359  if (ca.isbig)
361  cb = mult128 (b.num, lcd / b.denom);
362  if (cb.isbig)
364  cab = add128 (ca, cb);
365  if (cab.isbig)
367  sum.num = cab.lo;
368  if (cab.isneg)
369  sum.num = -sum.num;
370  sum.denom = lcd;
371  }
372 
373  if ((denom == QOF_DENOM_AUTO) &&
374  ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
375  {
376  denom = qof_numeric_lcd (a, b);
377  how = how & QOF_NUMERIC_RND_MASK;
378  }
379 
380  return qof_numeric_convert (sum, denom, how);
381 }
static QofNumeric qof_numeric_add_fixed ( QofNumeric  a,
QofNumeric  b 
)
inlinestatic

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.

412 {
413  return qof_numeric_add (a, b, QOF_DENOM_AUTO,
415 }
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.

1016 {
1017 
1018  QofNumeric sum = qof_numeric_add (a, b, denom, how);
1019  QofNumeric exact = qof_numeric_add (a, b, QOF_DENOM_AUTO,
1021  QofNumeric err = qof_numeric_sub (sum, exact, QOF_DENOM_AUTO,
1023 
1024  if (error)
1025  *error = err;
1026  return sum;
1027 }
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.

45 {
46  if (in.denom != 0)
47  return QOF_ERROR_OK;
48  else if (in.num)
49  {
50  if ((0 < in.num) || (-4 > in.num))
51  in.num = (gint64) QOF_ERROR_OVERFLOW;
52  return (QofNumericErrorCode) in.num;
53  }
54  else
55  return QOF_ERROR_ARG;
56 }
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.

175 {
176  gint64 aa, bb;
177  QofInt128 l, r;
178 
179  if (qof_numeric_check (a) || qof_numeric_check (b))
180  return 0;
181 
182  if (a.denom == b.denom)
183  {
184  if (a.num == b.num)
185  return 0;
186  if (a.num > b.num)
187  return 1;
188  return -1;
189  }
190 
191  if ((a.denom > 0) && (b.denom > 0))
192  {
193  /* Avoid overflows using 128-bit intermediate math */
194  l = mult128 (a.num, b.denom);
195  r = mult128 (b.num, a.denom);
196  return cmp128 (l, r);
197  }
198 
199  if (a.denom < 0)
200  a.denom *= -1;
201  if (b.denom < 0)
202  b.denom *= -1;
203 
204  /* BUG: Possible overflow here.. Also, doesn't properly deal with
205  * reciprocal denominators.
206  */
207  aa = a.num * a.denom;
208  bb = b.num * b.denom;
209 
210  if (aa == bb)
211  return 0;
212  if (aa > bb)
213  return 1;
214  return -1;
215 }
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.

663 {
664  QofNumeric out;
665  QofNumeric temp;
666  gint64 temp_bc;
667  gint64 temp_a;
668  gint64 remainder;
669  gint64 sign;
670  gint denom_neg = 0;
671  gdouble ratio, logratio;
672  gdouble sigfigs;
673  QofInt128 nume, newm;
674 
675  temp.num = 0;
676  temp.denom = 0;
677 
678  if (qof_numeric_check (in))
680 
681  if (denom == QOF_DENOM_AUTO)
682  {
683  switch (how & QOF_NUMERIC_DENOM_MASK)
684  {
685  default:
686  case QOF_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */
687  case QOF_HOW_DENOM_EXACT:
688  return in;
689  break;
690 
692  /* reduce the input to a relatively-prime fraction */
693  return qof_numeric_reduce (in);
694  break;
695 
696  case QOF_HOW_DENOM_FIXED:
697  if (in.denom != denom)
699  else
700  return in;
701  break;
702 
704  ratio = fabs (qof_numeric_to_double (in));
705  if (ratio < 10e-20)
706  logratio = 0;
707  else
708  {
709  logratio = log10 (ratio);
710  logratio = ((logratio > 0.0) ?
711  (floor (logratio) + 1.0) : (ceil (logratio)));
712  }
713  sigfigs = QOF_HOW_GET_SIGFIGS (how);
714 
715  if (sigfigs - logratio >= 0)
716  denom = (gint64) (pow (10, sigfigs - logratio));
717  else
718  denom = -((gint64) (pow (10, logratio - sigfigs)));
719 
720  how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_NUMERIC_SIGFIGS_MASK;
721  break;
722  }
723  }
724 
725  /* Make sure we need to do the work */
726  if (in.denom == denom)
727  return in;
728  if (in.num == 0)
729  {
730  out.num = 0;
731  out.denom = denom;
732  return out;
733  }
734 
735  /* If the denominator of the input value is negative, get rid of that. */
736  if (in.denom < 0)
737  {
738  in.num = in.num * (-in.denom); /* BUG: overflow not handled. */
739  in.denom = 1;
740  }
741 
742  sign = (in.num < 0) ? -1 : 1;
743 
744  /* If the denominator is less than zero, we are to interpret it as
745  * the reciprocal of its magnitude. */
746  if (denom < 0)
747  {
748 
749  /* XXX FIXME: use 128-bit math here ... */
750  denom = -denom;
751  denom_neg = 1;
752  temp_a = (in.num < 0) ? -in.num : in.num;
753  temp_bc = in.denom * denom; /* BUG: overflow not handled. */
754  remainder = temp_a % temp_bc;
755  out.num = temp_a / temp_bc;
756  out.denom = -denom;
757  }
758  else
759  {
760  /* Do all the modulo and int division on positive values to make
761  * things a little clearer. Reduce the fraction denom/in.denom to
762  * help with range errors */
763  temp.num = denom;
764  temp.denom = in.denom;
765  temp = qof_numeric_reduce (temp);
766 
767  /* Symbolically, do the following:
768  * out.num = in.num * temp.num;
769  * remainder = out.num % temp.denom;
770  * out.num = out.num / temp.denom;
771  * out.denom = denom;
772  */
773  nume = mult128 (in.num, temp.num);
774  newm = div128 (nume, temp.denom);
775  remainder = rem128 (nume, temp.denom);
776 
777  if (newm.isbig)
779 
780  out.num = newm.lo;
781  out.denom = denom;
782  }
783 
784  if (remainder)
785  {
786  switch (how & QOF_NUMERIC_RND_MASK)
787  {
788  case QOF_HOW_RND_FLOOR:
789  if (sign < 0)
790  out.num = out.num + 1;
791  break;
792 
793  case QOF_HOW_RND_CEIL:
794  if (sign > 0)
795  out.num = out.num + 1;
796  break;
797 
798  case QOF_HOW_RND_TRUNC:
799  break;
800 
801  case QOF_HOW_RND_PROMOTE:
802  out.num = out.num + 1;
803  break;
804 
806  if (denom_neg)
807  {
808  if ((2 * remainder) > in.denom * denom)
809  out.num = out.num + 1;
810  }
811  else if ((2 * remainder) > temp.denom)
812  out.num = out.num + 1;
813  /* check that 2*remainder didn't over-flow */
814  else if (((2 * remainder) < remainder) &&
815  (remainder > (temp.denom / 2)))
816  out.num = out.num + 1;
817  break;
818 
820  if (denom_neg)
821  {
822  if ((2 * remainder) >= in.denom * denom)
823  out.num = out.num + 1;
824  }
825  else if ((2 * remainder) >= temp.denom)
826  out.num = out.num + 1;
827  /* check that 2*remainder didn't over-flow */
828  else if (((2 * remainder) < remainder) &&
829  (remainder >= (temp.denom / 2)))
830  out.num = out.num + 1;
831  break;
832 
833  case QOF_HOW_RND_ROUND:
834  if (denom_neg)
835  {
836  if ((2 * remainder) > in.denom * denom)
837  out.num = out.num + 1;
838  else if ((2 * remainder) == in.denom * denom)
839  {
840  if (out.num % 2)
841  out.num = out.num + 1;
842  }
843  }
844  else
845  {
846  if ((2 * remainder) > temp.denom)
847  out.num = out.num + 1;
848  /* check that 2*remainder didn't over-flow */
849  else if (((2 * remainder) < remainder) &&
850  (remainder > (temp.denom / 2)))
851  {
852  out.num = out.num + 1;
853  }
854  else if ((2 * remainder) == temp.denom)
855  {
856  if (out.num % 2)
857  out.num = out.num + 1;
858  }
859  /* check that 2*remainder didn't over-flow */
860  else if (((2 * remainder) < remainder) &&
861  (remainder == (temp.denom / 2)))
862  {
863  if (out.num % 2)
864  out.num = out.num + 1;
865  }
866  }
867  break;
868 
869  case QOF_HOW_RND_NEVER:
871  break;
872  }
873  }
874 
875  out.num = (sign > 0) ? out.num : (-out.num);
876 
877  return out;
878 }
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 
)
inlinestatic

Make a QofNumeric from numerator and denominator

Definition at line 243 of file qofnumeric.h.

244 {
245  QofNumeric out;
246  out.num = num;
247  out.denom = denom;
248  return out;
249 }
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.

1104 {
1105  static gchar buff[1000];
1106  static gchar *p = buff;
1107  gint64 tmpnum = n.num;
1108  gint64 tmpdenom = n.denom;
1109 
1110  p += 100;
1111  if (p - buff >= 1000)
1112  p = buff;
1113 
1114  sprintf (p, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum,
1115  tmpdenom);
1116 
1117  return p;
1118 }
static gint64 qof_numeric_denom ( QofNumeric  a)
inlinestatic

Return denominator

Definition at line 291 of file qofnumeric.h.

292 {
293  return a.denom;
294 }
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.

517 {
518  QofNumeric quotient;
519  QofInt128 nume, deno;
520 
521  if (qof_numeric_check (a) || qof_numeric_check (b))
523 
524  if ((denom == QOF_DENOM_AUTO) &&
525  (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED)
526  {
527  if (a.denom == b.denom)
528  denom = a.denom;
529  else if (a.denom == 0)
530  denom = b.denom;
531  else
533  }
534 
535  if (a.denom < 0)
536  {
537  a.num *= -a.denom; /* BUG: overflow not handled. */
538  a.denom = 1;
539  }
540 
541  if (b.denom < 0)
542  {
543  b.num *= -b.denom; /* BUG: overflow not handled. */
544  b.denom = 1;
545  }
546 
547  if (a.denom == b.denom)
548  {
549  quotient.num = a.num;
550  quotient.denom = b.num;
551  }
552  else
553  {
554  gint64 sgn = 1;
555  if (0 > a.num)
556  {
557  sgn = -sgn;
558  a.num = -a.num;
559  }
560  if (0 > b.num)
561  {
562  sgn = -sgn;
563  b.num = -b.num;
564  }
565  nume = mult128 (a.num, b.denom);
566  deno = mult128 (b.num, a.denom);
567 
568  /* Try to avoid overflow by removing common factors */
569  if (nume.isbig && deno.isbig)
570  {
573  gint64 gcf_nume = gcf64 (ra.num, rb.num);
574  gint64 gcf_deno = gcf64 (rb.denom, ra.denom);
575 
576  nume = mult128 (ra.num / gcf_nume, rb.denom / gcf_deno);
577  deno = mult128 (rb.num / gcf_nume, ra.denom / gcf_deno);
578  }
579 
580  if ((0 == nume.isbig) && (0 == deno.isbig))
581  {
582  quotient.num = sgn * nume.lo;
583  quotient.denom = deno.lo;
584  goto dive_done;
585  }
586  else if (0 == deno.isbig)
587  {
588  quotient = reduce128 (nume, deno.lo);
589  if (0 == qof_numeric_check (quotient))
590  {
591  quotient.num *= sgn;
592  goto dive_done;
593  }
594  }
595 
596  /* If rounding allowed, then shift until there's no
597  * more overflow. The conversion at the end will fix
598  * things up for the final value. */
601  while (nume.isbig || deno.isbig)
602  {
603  nume = shift128 (nume);
604  deno = shift128 (deno);
605  }
606  quotient.num = sgn * nume.lo;
607  quotient.denom = deno.lo;
608  if (0 == quotient.denom)
609  {
611  }
612  }
613 
614  if (quotient.denom < 0)
615  {
616  quotient.num = -quotient.num;
617  quotient.denom = -quotient.denom;
618  }
619 
620  dive_done:
621  if ((denom == QOF_DENOM_AUTO) &&
622  ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
623  {
624  denom = qof_numeric_lcd (a, b);
625  how = how & QOF_NUMERIC_RND_MASK;
626  }
627 
628  return qof_numeric_convert (quotient, denom, how);
629 }
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.

1073 {
1074  QofNumeric quot = qof_numeric_div (a, b, denom, how);
1075  QofNumeric exact = qof_numeric_div (a, b, QOF_DENOM_AUTO,
1077  QofNumeric err = qof_numeric_sub (quot, exact,
1079  if (error)
1080  *error = err;
1081  return quot;
1082 }
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.

224 {
225  return ((a.num == b.num) && (a.denom == b.denom));
226 }
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.

234 {
235  QofInt128 l, r;
236 
237  if ((a.denom == b.denom) && (a.denom > 0))
238  return (a.num == b.num);
239  if ((a.denom > 0) && (b.denom > 0))
240  {
241  // return (a.num*b.denom == b.num*a.denom);
242  l = mult128 (a.num, b.denom);
243  r = mult128 (b.num, a.denom);
244  return equal128 (l, r);
245 
246 #if ALT_WAY_OF_CHECKING_EQUALITY
247  QofNumeric ra = QofNumeric_reduce (a);
248  QofNumeric rb = QofNumeric_reduce (b);
249  if (ra.denom != rb.denom)
250  return 0;
251  if (ra.num != rb.num)
252  return 0;
253  return 1;
254 #endif
255  }
256  if ((a.denom < 0) && (b.denom < 0))
257  {
258  l = mult128 (a.num, -a.denom);
259  r = mult128 (b.num, -b.denom);
260  return equal128 (l, r);
261  }
262  else
263  {
264  /* BUG: One of the numbers has a reciprocal denom, and the other
265  does not. I just don't know to handle this case in any
266  reasonably overflow-proof yet simple way. So, this funtion
267  will simply get it wrong whenever the three multiplies
268  overflow 64-bits. -CAS */
269  if (a.denom < 0)
270  return ((a.num * -a.denom * b.denom) == b.num);
271  else
272  return (a.num == (b.num * a.denom * -b.denom));
273  }
274  return ((a.num * b.denom) == (a.denom * b.num));
275 }
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.

1005 {
1006  return qof_numeric_create (error_code, 0LL);
1007 }
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.

920 {
921  QofNumeric out;
922  gint64 int_part = 0;
923  gdouble frac_part;
924  gint64 frac_int = 0;
925  gdouble logval;
926  gdouble sigfigs;
927 
928  if ((denom == QOF_DENOM_AUTO) && (how & QOF_HOW_DENOM_SIGFIG))
929  {
930  if (fabs (in) < 10e-20)
931  logval = 0;
932  else
933  {
934  logval = log10 (fabs (in));
935  logval = ((logval > 0.0) ?
936  (floor (logval) + 1.0) : (ceil (logval)));
937  }
938  sigfigs = QOF_HOW_GET_SIGFIGS (how);
939  if (sigfigs - logval >= 0)
940  denom = (gint64) (pow (10, sigfigs - logval));
941  else
942  denom = -((gint64) (pow (10, logval - sigfigs)));
943 
944  how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_NUMERIC_SIGFIGS_MASK;
945  }
946 
947  int_part = (gint64) (floor (fabs (in)));
948  frac_part = in - (double) int_part;
949 
950  int_part = int_part * denom;
951  frac_part = frac_part * (double) denom;
952 
953  switch (how & QOF_NUMERIC_RND_MASK)
954  {
955  case QOF_HOW_RND_FLOOR:
956  frac_int = (gint64) floor (frac_part);
957  break;
958 
959  case QOF_HOW_RND_CEIL:
960  frac_int = (gint64) ceil (frac_part);
961  break;
962 
963  case QOF_HOW_RND_TRUNC:
964  frac_int = (gint64) frac_part;
965  break;
966 
967  case QOF_HOW_RND_ROUND:
969  frac_int = (gint64) rint (frac_part);
970  break;
971 
972  case QOF_HOW_RND_NEVER:
973  frac_int = (gint64) floor (frac_part);
974  if (frac_part != (double) frac_int)
975  {
976  /* signal an error */
977  }
978  break;
979  }
980 
981  out.num = int_part + frac_int;
982  out.denom = denom;
983  return out;
984 }
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.

1122 {
1123  size_t num_read;
1124  gint64 tmpnum;
1125  gint64 tmpdenom;
1126 
1127  if (!str)
1128  return FALSE;
1129 
1130 #ifdef QOF_DEPRECATED
1131  /* must use "<" here because %n's effects aren't well defined */
1132  if (sscanf (str, " " QOF_SCANF_LLD "/" QOF_SCANF_LLD "%n",
1133  &tmpnum, &tmpdenom, &num_read) < 2)
1134  {
1135  return FALSE;
1136  }
1137 #else
1138  tmpnum = strtoll (str, NULL, 0);
1139  str = strchr (str, '/');
1140  if (!str)
1141  return FALSE;
1142  str++;
1143  tmpdenom = strtoll (str, NULL, 0);
1144  num_read = strspn (str, "0123456789");
1145 #endif
1146  n->num = tmpnum;
1147  n->denom = tmpdenom;
1148  return TRUE;
1149 }
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.

406 {
407  QofNumeric product, result;
408  QofInt128 bignume, bigdeno;
409 
410  if (qof_numeric_check (a) || qof_numeric_check (b))
412 
413  if ((denom == QOF_DENOM_AUTO) &&
414  (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED)
415  {
416  if (a.denom == b.denom)
417  denom = a.denom;
418  else if (b.num == 0)
419  denom = a.denom;
420  else if (a.num == 0)
421  denom = b.denom;
422  else
424  }
425 
426  if ((denom == QOF_DENOM_AUTO) &&
427  ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD))
428  {
429  denom = qof_numeric_lcd (a, b);
430  how = how & QOF_NUMERIC_RND_MASK;
431  }
432 
433  if (a.denom < 0)
434  {
435  a.num *= -a.denom; /* BUG: overflow not handled. */
436  a.denom = 1;
437  }
438 
439  if (b.denom < 0)
440  {
441  b.num *= -b.denom; /* BUG: overflow not handled. */
442  b.denom = 1;
443  }
444 
445  bignume = mult128 (a.num, b.num);
446  bigdeno = mult128 (a.denom, b.denom);
447  product.num = a.num * b.num;
448  product.denom = a.denom * b.denom;
449 
450  /* If it looks to be overflowing, try to reduce the fraction ... */
451  if (bignume.isbig || bigdeno.isbig)
452  {
453  gint64 tmp;
454 
455  a = qof_numeric_reduce (a);
456  b = qof_numeric_reduce (b);
457  tmp = a.num;
458  a.num = b.num;
459  b.num = tmp;
460  a = qof_numeric_reduce (a);
461  b = qof_numeric_reduce (b);
462  bignume = mult128 (a.num, b.num);
463  bigdeno = mult128 (a.denom, b.denom);
464  product.num = a.num * b.num;
465  product.denom = a.denom * b.denom;
466  }
467 
468  /* If it its still overflowing, and rounding is allowed then round */
469  if (bignume.isbig || bigdeno.isbig)
470  {
471  /* If rounding allowed, then shift until there's no
472  * more overflow. The conversion at the end will fix
473  * things up for the final value. Else overflow. */
475  {
476  if (bigdeno.isbig)
478  product = reduce128 (bignume, product.denom);
479  if (qof_numeric_check (product))
481  }
482  else
483  {
484  while (bignume.isbig || bigdeno.isbig)
485  {
486  bignume = shift128 (bignume);
487  bigdeno = shift128 (bigdeno);
488  }
489  product.num = bignume.lo;
490  if (bignume.isneg)
491  product.num = -product.num;
492 
493  product.denom = bigdeno.lo;
494  if (0 == product.denom)
496  }
497  }
498 
499 #if 0 /* currently, product denom won't ever be zero */
500  if (product.denom < 0)
501  {
502  product.num = -product.num;
503  product.denom = -product.denom;
504  }
505 #endif
506 
507  result = qof_numeric_convert (product, denom, how);
508  return result;
509 }
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.

1054 {
1055  QofNumeric prod = qof_numeric_mul (a, b, denom, how);
1056  QofNumeric exact = qof_numeric_mul (a, b, QOF_DENOM_AUTO,
1058  QofNumeric err = qof_numeric_sub (prod, exact, QOF_DENOM_AUTO,
1060  if (error)
1061  *error = err;
1062  return prod;
1063 }
QofNumeric qof_numeric_neg ( QofNumeric  a)

Negate the argument

Definition at line 637 of file qofnumeric.c.

638 {
639  if (qof_numeric_check (a))
641  return qof_numeric_create (-a.num, a.denom);
642 }
gboolean qof_numeric_negative_p ( QofNumeric  a)

Returns 1 if a < 0, otherwise returns 0.

Definition at line 137 of file qofnumeric.c.

138 {
139  if (qof_numeric_check (a))
140  return 0;
141  else
142  {
143  if ((a.num < 0) && (a.denom != 0))
144  return 1;
145  else
146  return 0;
147  }
148 }
static gint64 qof_numeric_num ( QofNumeric  a)
inlinestatic

Return numerator

Definition at line 284 of file qofnumeric.h.

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

Returns 1 if a > 0, otherwise returns 0.

Definition at line 155 of file qofnumeric.c.

156 {
157  if (qof_numeric_check (a))
158  return 0;
159  else
160  {
161  if ((a.num > 0) && (a.denom != 0))
162  return 1;
163  else
164  return 0;
165  }
166 }
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.

889 {
890  gint64 t;
891  gint64 num = (in.num < 0) ? (-in.num) : in.num;
892  gint64 denom = in.denom;
893  QofNumeric out;
894 
895  if (qof_numeric_check (in))
897 
898  /* The strategy is to use Euclid's algorithm */
899  while (denom > 0)
900  {
901  t = num % denom;
902  num = denom;
903  denom = t;
904  }
905  /* num now holds the GCD (Greatest Common Divisor) */
906 
907  /* All calculations are done on positive num, since it's not
908  * well defined what % does for negative values */
909  out.num = in.num / num;
910  out.denom = in.denom / num;
911  return out;
912 }
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.

286 {
287  QofNumeric aconv, bconv;
288 
289  aconv = qof_numeric_convert (a, denom, how);
290  bconv = qof_numeric_convert (b, denom, how);
291 
292  return (qof_numeric_equal (aconv, bconv));
293 }
QofNumeric qof_numeric_sub ( QofNumeric  a,
QofNumeric  b,
gint64  denom,
gint  how 
)

Return a-b.

Definition at line 388 of file qofnumeric.c.

389 {
390  QofNumeric nb;
391 
392  if (qof_numeric_check (a) || qof_numeric_check (b))
394 
395  nb = b;
396  nb.num = -nb.num;
397  return qof_numeric_add (a, nb, denom, how);
398 }
static QofNumeric qof_numeric_sub_fixed ( QofNumeric  a,
QofNumeric  b 
)
inlinestatic

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.

423 {
424  return qof_numeric_sub (a, b, QOF_DENOM_AUTO,
426 }
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.

1036 {
1037  QofNumeric diff = qof_numeric_sub (a, b, denom, how);
1038  QofNumeric exact = qof_numeric_sub (a, b, QOF_DENOM_AUTO,
1040  QofNumeric err = qof_numeric_sub (diff, exact, QOF_DENOM_AUTO,
1042  if (error)
1043  *error = err;
1044  return diff;
1045 }
gdouble qof_numeric_to_double ( QofNumeric  in)

Convert numeric to floating-point value.

Definition at line 991 of file qofnumeric.c.

992 {
993  if (in.denom > 0)
994  return (gdouble) in.num / (gdouble) in.denom;
995  else
996  return (gdouble) (in.num * -in.denom);
997 }
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.

1090 {
1091  gchar *result;
1092  gint64 tmpnum = n.num;
1093  gint64 tmpdenom = n.denom;
1094 
1095  result =
1096  g_strdup_printf ("%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum,
1097  tmpdenom);
1098 
1099  return result;
1100 }
static QofNumeric qof_numeric_zero ( void  )
inlinestatic

create a zero-value QofNumeric

Definition at line 253 of file qofnumeric.h.

254 {
255  return qof_numeric_create (0, 1);
256 }
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.

120 {
121  if (qof_numeric_check (a))
122  return 0;
123  else
124  {
125  if ((a.num == 0) && (a.denom != 0))
126  return 1;
127  else
128  return 0;
129  }
130 }