Numeric: Rational Number Handling w/ Rounding Error Control
[Query Object Framework]


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


Files

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

Data Structures

struct  _gnc_numeric

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.

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 in, gint64 denom, gint how)
gboolean string_to_gnc_numeric (const gchar *str, gnc_numeric *n)
gnc_numeric gnc_numeric_error (GNCNumericErrorCode error_code)

Value Accessors

gdouble gnc_numeric_to_double (gnc_numeric in)
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 in, gint64 denom, gint how)
gnc_numeric gnc_numeric_convert_with_error (gnc_numeric in, gint64 denom, gint how, gnc_numeric *error)
gnc_numeric gnc_numeric_reduce (gnc_numeric in)

GValue

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

Deprecated, backwards-compatible definitions

#define GNC_RND_FLOOR   GNC_HOW_RND_FLOOR
#define GNC_RND_CEIL   GNC_HOW_RND_CEIL
#define GNC_RND_TRUNC   GNC_HOW_RND_TRUNC
#define GNC_RND_PROMOTE   GNC_HOW_RND_PROMOTE
#define GNC_RND_ROUND_HALF_DOWN   GNC_HOW_RND_ROUND_HALF_DOWN
#define GNC_RND_ROUND_HALF_UP   GNC_HOW_RND_ROUND_HALF_UP
#define GNC_RND_ROUND   GNC_HOW_RND_ROUND
#define GNC_RND_NEVER   GNC_HOW_RND_NEVER
#define GNC_DENOM_EXACT   GNC_HOW_DENOM_EXACT
#define GNC_DENOM_REDUCE   GNC_HOW_DENOM_REDUCE
#define GNC_DENOM_LCD   GNC_HOW_DENOM_LCD
#define GNC_DENOM_FIXED   GNC_HOW_DENOM_FIXED
#define GNC_DENOM_SIGFIG   GNC_HOW_DENOM_SIGFIG
#define GNC_DENOM_SIGFIGS(X)   GNC_HOW_DENOM_SIGFIGS(X)
#define GNC_NUMERIC_GET_SIGFIGS(X)   GNC_HOW_GET_SIGFIGS(X)

Typedefs

typedef _gnc_numeric gnc_numeric
 An rational-number type.


Define Documentation

#define GNC_DENOM_AUTO   0

Compute an appropriate denominator automatically. Flags in the 'how' argument will specify how to compute the denominator.

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

#define GNC_DENOM_RECIPROCAL (  )     (- ( a ))

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

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

#define GNC_HOW_DENOM_SIGFIGS (  )     ( ((( 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 204 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 119 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"?

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 132 of file gnc-numeric.h.

00132      { 
00134   GNC_HOW_RND_FLOOR            = 0x01, 
00135 
00137   GNC_HOW_RND_CEIL             = 0x02,  
00138 
00140   GNC_HOW_RND_TRUNC            = 0x03,
00141 
00143   GNC_HOW_RND_PROMOTE          = 0x04,
00144 
00148   GNC_HOW_RND_ROUND_HALF_DOWN  = 0x05, 
00149 
00153   GNC_HOW_RND_ROUND_HALF_UP    = 0x06, 
00154 
00160   GNC_HOW_RND_ROUND            = 0x07, 
00161 
00165   GNC_HOW_RND_NEVER            = 0x08
00166 };

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 169 of file gnc-numeric.h.

00169      { 
00175   GNC_HOW_DENOM_EXACT  = 0x10, 
00176 
00182   GNC_HOW_DENOM_REDUCE = 0x20,
00183 
00187   GNC_HOW_DENOM_LCD    = 0x30,
00188 
00193   GNC_HOW_DENOM_FIXED  = 0x40,
00194 
00198   GNC_HOW_DENOM_SIGFIG = 0x50
00199 };

enum GNCNumericErrorCode

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 208 of file gnc-numeric.h.

00208              {
00209   GNC_ERROR_OK         =  0,   
00210   GNC_ERROR_ARG        = -1,   
00211   GNC_ERROR_OVERFLOW   = -2,   
00214   GNC_ERROR_DENOM_DIFF = -3,   
00215 
00218   GNC_ERROR_REMAINDER  = -4   
00219 } GNCNumericErrorCode;


Function Documentation

gnc_numeric double_to_gnc_numeric ( double  in,
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 explicitctly or through sigfigs.

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

01028 {
01029   gnc_numeric out;
01030   gint64 int_part=0;
01031   double frac_part;
01032   gint64 frac_int=0;
01033   double logval; 
01034   double sigfigs;
01035 
01036   if((denom == GNC_DENOM_AUTO) && (how & GNC_HOW_DENOM_SIGFIG)) 
01037   {
01038     if(fabs(in) < 10e-20) {
01039       logval = 0;
01040     }
01041     else {
01042       logval   = log10(fabs(in));
01043       logval   = ((logval > 0.0) ? 
01044                   (floor(logval)+1.0) : (ceil(logval)));
01045     }
01046     sigfigs  = GNC_HOW_GET_SIGFIGS(how);
01047     if(sigfigs-logval >= 0) {
01048       denom    = (gint64)(pow(10, sigfigs-logval));
01049     }
01050     else {
01051       denom    = -((gint64)(pow(10, logval-sigfigs)));
01052     }
01053 
01054     how =  how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK;
01055   }
01056 
01057   int_part  = (gint64)(floor(fabs(in)));
01058   frac_part = in - (double)int_part;
01059   
01060   int_part = int_part * denom;
01061   frac_part = frac_part * (double)denom;
01062 
01063   switch(how & GNC_NUMERIC_RND_MASK) {
01064   case GNC_HOW_RND_FLOOR:
01065     frac_int = (gint64)floor(frac_part);
01066     break;
01067 
01068   case GNC_HOW_RND_CEIL:
01069     frac_int = (gint64)ceil(frac_part);
01070     break;
01071 
01072   case GNC_HOW_RND_TRUNC:
01073     frac_int = (gint64)frac_part;
01074     break;
01075     
01076   case GNC_HOW_RND_ROUND:
01077   case GNC_HOW_RND_ROUND_HALF_UP:
01078     frac_int = (gint64)rint(frac_part);
01079     break;
01080 
01081   case GNC_HOW_RND_NEVER:
01082     frac_int = (gint64)floor(frac_part);
01083     if(frac_part != (double) frac_int) {
01084       /* signal an error */
01085     }
01086     break;
01087   }
01088 
01089   out.num   = int_part + frac_int; 
01090   out.denom = denom;
01091   return out;
01092 }

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 1223 of file gnc-numeric.c.

01224 {
01225   static char buff[1000];
01226   static char *p = buff;
01227   gint64 tmpnum = n.num;
01228   gint64 tmpdenom = n.denom;
01229 
01230   p+= 100;
01231   if (p-buff >= 1000) p = buff;
01232    
01233   sprintf(p, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, tmpdenom);
01234 
01235   return p;
01236 }

gnc_numeric gnc_numeric_abs ( gnc_numeric  a  ) 

Return the absolute value of the argument

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

00723 {
00724   if(gnc_numeric_check(a)) {
00725     return gnc_numeric_error(GNC_ERROR_ARG);
00726   }
00727   return gnc_numeric_create(ABS(a.num), a.denom);
00728 }

gnc_numeric gnc_numeric_add ( gnc_numeric  a,
gnc_numeric  b,
gint64  denom,
gint  how 
)

Return a+b.

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

00349 {
00350   gnc_numeric sum;
00351   
00352   if(gnc_numeric_check(a) || gnc_numeric_check(b)) 
00353   {
00354     return gnc_numeric_error(GNC_ERROR_ARG);
00355   }
00356 
00357   if((denom == GNC_DENOM_AUTO) && 
00358      (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) 
00359   {
00360     if(a.denom == b.denom) {
00361       denom = a.denom;
00362     }
00363     else if(b.num == 0) {
00364       denom = a.denom;
00365       b.denom = a.denom;
00366     }
00367     else if(a.num == 0) {
00368       denom = b.denom;
00369       a.denom = b.denom;
00370     }
00371     else {
00372       return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
00373     }
00374   }
00375   
00376   if(a.denom < 0) 
00377   {
00378     a.num *= -a.denom;  /* BUG: overflow not handled.  */
00379     a.denom = 1;
00380   }
00381 
00382   if(b.denom < 0) 
00383   {
00384     b.num *= -b.denom;  /* BUG: overflow not handled.  */
00385     b.denom = 1;
00386   }
00387 
00388   /* Get an exact answer.. same denominator is the common case. */ 
00389   if(a.denom == b.denom) 
00390   {
00391     sum.num = a.num + b.num;  /* BUG: overflow not handled.  */
00392     sum.denom = a.denom;
00393   }
00394   else 
00395   {
00396     /* We want to do this:
00397      *    sum.num = a.num*b.denom + b.num*a.denom;
00398      *    sum.denom = a.denom*b.denom;
00399      * but the multiply could overflow.  
00400      * Computing the LCD minimizes likelihood of overflow
00401      */
00402     gint64 lcd;
00403     qofint128 ca, cb, cab;
00404     lcd = gnc_numeric_lcd(a,b);
00405     if (GNC_ERROR_ARG == lcd)
00406     {
00407        return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00408     }
00409     ca = mult128 (a.num, lcd/a.denom);
00410     if (ca.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00411 
00412     cb = mult128 (b.num, lcd/b.denom);
00413     if (cb.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00414 
00415     cab = add128 (ca, cb);
00416     if (cab.isbig) return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00417     
00418     sum.num   = cab.lo;
00419     if (cab.isneg) sum.num = -sum.num;
00420     sum.denom = lcd;
00421   }
00422   
00423   if((denom == GNC_DENOM_AUTO) &&
00424      ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) 
00425   {
00426     denom = gnc_numeric_lcd(a, b);
00427     how   = how & GNC_NUMERIC_RND_MASK;
00428   }
00429   
00430   return gnc_numeric_convert(sum, denom, how);                             
00431 }

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 1127 of file gnc-numeric.c.

01130 {
01131 
01132   gnc_numeric sum   = gnc_numeric_add(a, b, denom, how);
01133   gnc_numeric exact = gnc_numeric_add(a, b, GNC_DENOM_AUTO, 
01134                                       GNC_HOW_DENOM_REDUCE);
01135   gnc_numeric err   = gnc_numeric_sub(sum, exact, GNC_DENOM_AUTO,
01136                                       GNC_HOW_DENOM_REDUCE);
01137 
01138   if(error) {
01139     *error = err;
01140   }
01141   return sum;
01142 }

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 59 of file gnc-numeric.c.

00060 {
00061   if(in.denom != 0) 
00062   {
00063     return GNC_ERROR_OK;
00064   }
00065   else if(in.num) 
00066   {
00067     if ((0 < in.num) || (-4 > in.num))
00068     {
00069        in.num = (gint64) GNC_ERROR_OVERFLOW;
00070     }
00071     return (GNCNumericErrorCode) in.num;
00072   }
00073   else 
00074   {
00075     return GNC_ERROR_ARG;
00076   }
00077 }

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 222 of file gnc-numeric.c.

00223 {
00224   gint64 aa, bb;
00225   qofint128 l, r;
00226 
00227   if(gnc_numeric_check(a) || gnc_numeric_check(b)) 
00228   {
00229     return 0;
00230   }
00231 
00232   if (a.denom == b.denom)
00233   {
00234     if(a.num == b.num) return 0;
00235     if(a.num > b.num) return 1;
00236     return -1;
00237   }
00238 
00239   if  ((a.denom > 0) && (b.denom > 0))
00240   {
00241     /* Avoid overflows using 128-bit intermediate math */
00242     l = mult128 (a.num, b.denom);
00243     r = mult128 (b.num, a.denom);
00244     return cmp128 (l,r);
00245   }
00246 
00247   if (a.denom < 0)
00248       a.denom *= -1;
00249   if (b.denom < 0)
00250       b.denom *= -1;
00251 
00252   /* BUG: Possible overflow here..  Also, doesn't properly deal with
00253    * reciprocal denominators.
00254    */
00255   aa = a.num * a.denom;
00256   bb = b.num * b.denom;
00257 
00258   if(aa == bb) return 0;
00259   if(aa > bb) return 1;
00260   return -1;
00261 }

gnc_numeric gnc_numeric_convert ( gnc_numeric  in,
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 735 of file gnc-numeric.c.

00736 {
00737   gnc_numeric out;
00738   gnc_numeric temp;
00739   gint64      temp_bc;
00740   gint64      temp_a;
00741   gint64      remainder;  
00742   gint64      sign;
00743   gint        denom_neg=0;
00744   double      ratio, logratio;
00745   double      sigfigs;
00746   qofint128 nume, newm;
00747 
00748   temp.num   = 0;
00749   temp.denom = 0;
00750 
00751   if(gnc_numeric_check(in)) {
00752     return gnc_numeric_error(GNC_ERROR_ARG);
00753   }
00754   
00755   if(denom == GNC_DENOM_AUTO) 
00756   {
00757     switch(how & GNC_NUMERIC_DENOM_MASK) 
00758     {
00759     default:
00760     case GNC_HOW_DENOM_LCD:   /* LCD is meaningless with AUTO in here */
00761     case GNC_HOW_DENOM_EXACT:
00762       return in;
00763       break;
00764       
00765     case GNC_HOW_DENOM_REDUCE:
00766       /* reduce the input to a relatively-prime fraction */
00767       return gnc_numeric_reduce(in);
00768       break;
00769       
00770     case GNC_HOW_DENOM_FIXED:
00771       if(in.denom != denom) {
00772         return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
00773       }
00774       else {
00775         return in;
00776       }
00777       break;
00778       
00779     case GNC_HOW_DENOM_SIGFIG:
00780       ratio    = fabs(gnc_numeric_to_double(in));
00781       if(ratio < 10e-20) {
00782         logratio = 0;
00783       }
00784       else {
00785         logratio = log10(ratio);
00786         logratio = ((logratio > 0.0) ? 
00787                     (floor(logratio)+1.0) : (ceil(logratio)));
00788       }
00789       sigfigs  = GNC_HOW_GET_SIGFIGS(how);
00790 
00791       if (fabs(sigfigs - logratio) > 18)
00792           return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00793 
00794       if(sigfigs-logratio >= 0) {
00795         denom    = (gint64)(pow(10, sigfigs-logratio));
00796       }
00797       else {
00798         denom    = -((gint64)(pow(10, logratio-sigfigs)));
00799       }
00800       
00801       how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK;
00802       break;
00803 
00804     }
00805   }
00806   
00807   /* Make sure we need to do the work */
00808   if(in.denom == denom) {
00809     return in;
00810   }
00811   if(in.num == 0) {
00812     out.num = 0;
00813     out.denom = denom;
00814     return out;
00815   }
00816   
00817   /* If the denominator of the input value is negative, get rid of that. */
00818   if(in.denom < 0) {
00819     in.num = in.num * (- in.denom);  /* BUG: overflow not handled.  */
00820     in.denom = 1;
00821   }
00822   
00823   sign = (in.num < 0) ? -1 : 1;
00824 
00825   /* If the denominator is less than zero, we are to interpret it as 
00826    * the reciprocal of its magnitude. */
00827   if(denom < 0) 
00828   {
00829 
00830     /* XXX FIXME: use 128-bit math here ... */
00831     denom     = - denom;
00832     denom_neg = 1;
00833     temp_a    = (in.num < 0) ? -in.num : in.num;
00834     temp_bc   = in.denom * denom;  /* BUG: overflow not handled.  */
00835     remainder = temp_a % temp_bc;
00836     out.num   = temp_a / temp_bc;
00837     out.denom = - denom;    
00838   }
00839   else 
00840   {
00841     /* Do all the modulo and int division on positive values to make
00842      * things a little clearer. Reduce the fraction denom/in.denom to
00843      * help with range errors */
00844     temp.num   = denom;
00845     temp.denom = in.denom;
00846     temp       = gnc_numeric_reduce(temp);
00847 
00848     /* Symbolically, do the following:
00849      * out.num   = in.num * temp.num;
00850      * remainder = out.num % temp.denom;
00851      * out.num   = out.num / temp.denom;
00852      * out.denom = denom;
00853      */
00854     nume = mult128 (in.num, temp.num);
00855     newm = div128 (nume, temp.denom);
00856     remainder = rem128 (nume, temp.denom);
00857 
00858     if (newm.isbig)
00859     {
00860        return gnc_numeric_error(GNC_ERROR_OVERFLOW);
00861     }
00862 
00863     out.num = newm.lo;
00864     out.denom = denom;
00865   }
00866 
00867   if (remainder) 
00868   {
00869     switch(how & GNC_NUMERIC_RND_MASK) 
00870     {
00871     case GNC_HOW_RND_FLOOR:
00872       if(sign < 0) {
00873         out.num = out.num + 1;
00874       }
00875       break;
00876       
00877     case GNC_HOW_RND_CEIL:
00878       if(sign > 0) {
00879         out.num = out.num + 1;
00880       }
00881       break;
00882       
00883     case GNC_HOW_RND_TRUNC:
00884       break;
00885 
00886     case GNC_HOW_RND_PROMOTE:
00887       out.num = out.num + 1;
00888       break;
00889       
00890     case GNC_HOW_RND_ROUND_HALF_DOWN:
00891       if(denom_neg) 
00892       {
00893         if((2 * remainder) > in.denom*denom) 
00894         {
00895           out.num = out.num + 1;
00896         }
00897       }
00898       else if((2 * remainder) > temp.denom) 
00899       {
00900         out.num = out.num + 1;
00901       }
00902       /* check that 2*remainder didn't over-flow */
00903       else if (((2 * remainder) < remainder) && 
00904                (remainder > (temp.denom / 2)))
00905       {
00906         out.num = out.num + 1;
00907       }
00908       break;
00909       
00910     case GNC_HOW_RND_ROUND_HALF_UP:
00911       if(denom_neg) 
00912       {
00913         if((2 * remainder) >= in.denom*denom) 
00914         {
00915           out.num = out.num + 1;
00916         }
00917       }
00918       else if((2 * remainder ) >= temp.denom) 
00919       {
00920         out.num = out.num + 1;
00921       }
00922       /* check that 2*remainder didn't over-flow */
00923       else if (((2 * remainder) < remainder) && 
00924                (remainder >= (temp.denom / 2)))
00925       {
00926         out.num = out.num + 1;
00927       }
00928       break;
00929       
00930     case GNC_HOW_RND_ROUND:
00931       if(denom_neg) 
00932       {
00933         if((2 * remainder) > in.denom*denom) 
00934         {
00935           out.num = out.num + 1;
00936         }
00937         else if((2 * remainder) == in.denom*denom) 
00938         {
00939           if(out.num % 2) 
00940           {
00941             out.num = out.num + 1;
00942           }
00943         }        
00944       }
00945       else 
00946       {
00947         if((2 * remainder ) > temp.denom) 
00948         {
00949           out.num = out.num + 1;
00950         }
00951         /* check that 2*remainder didn't over-flow */
00952         else if (((2 * remainder) < remainder) && 
00953                  (remainder > (temp.denom / 2)))
00954         {
00955           out.num = out.num + 1;
00956         }
00957         else if((2 * remainder) == temp.denom) 
00958         {
00959           if(out.num % 2) 
00960           {
00961             out.num = out.num + 1;
00962           }
00963         }    
00964         /* check that 2*remainder didn't over-flow */
00965         else if (((2 * remainder) < remainder) && 
00966                  (remainder ==  (temp.denom / 2)))
00967         {
00968           if(out.num % 2) 
00969           {
00970             out.num = out.num + 1;
00971           }
00972         }
00973       }    
00974       break;
00975       
00976     case GNC_HOW_RND_NEVER:
00977       return gnc_numeric_error(GNC_ERROR_REMAINDER);
00978       break;
00979     }
00980   }
00981   
00982   out.num = (sign > 0) ? out.num : (-out.num);
00983   
00984   return out;
00985 }

gnc_numeric gnc_numeric_convert_with_error ( gnc_numeric  in,
gint64  denom,
gint  how,
gnc_numeric error 
)

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

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 576 of file gnc-numeric.c.

00578 {
00579   gnc_numeric quotient;
00580   qofint128 nume, deno;
00581 
00582   if(gnc_numeric_check(a) || gnc_numeric_check(b)) 
00583   {
00584     return gnc_numeric_error(GNC_ERROR_ARG);
00585   }
00586 
00587   if((denom == GNC_DENOM_AUTO) && 
00588      (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) 
00589   {
00590     if(a.denom == b.denom) 
00591     {
00592       denom = a.denom;
00593     }
00594     else if(a.denom == 0) 
00595     {
00596       denom = b.denom;
00597     }
00598     else 
00599     {
00600       return gnc_numeric_error(GNC_ERROR_DENOM_DIFF);
00601     }
00602   }
00603   
00604 
00605   if(a.denom < 0) 
00606   {
00607     a.num *= -a.denom;   /* BUG: overflow not handled.  */
00608     a.denom = 1;
00609   }
00610 
00611   if(b.denom < 0) 
00612   {
00613     b.num *= -b.denom;   /* BUG: overflow not handled.  */
00614     b.denom = 1;
00615   }
00616 
00617   if(a.denom == b.denom) 
00618   {
00619     quotient.num = a.num;
00620     quotient.denom = b.num;
00621   }
00622   else 
00623   {
00624     gint64 sgn = 1;
00625     if (0 > a.num)
00626     {
00627       sgn = -sgn;
00628       a.num = -a.num;
00629     }
00630     if (0 > b.num)
00631     {
00632       sgn = -sgn;
00633       b.num = -b.num;
00634     }
00635     nume = mult128(a.num, b.denom);
00636     deno = mult128(b.num, a.denom);
00637 
00638     /* Try to avoid overflow by removing common factors */
00639     if (nume.isbig && deno.isbig)
00640     {
00641       gnc_numeric ra = gnc_numeric_reduce (a);
00642       gnc_numeric rb = gnc_numeric_reduce (b);
00643 
00644       gint64 gcf_nume = gcf64(ra.num, rb.num);
00645       gint64 gcf_deno = gcf64(rb.denom, ra.denom);
00646       nume = mult128(ra.num/gcf_nume, rb.denom/gcf_deno);
00647       deno = mult128(rb.num/gcf_nume, ra.denom/gcf_deno);
00648     }
00649 
00650     if ((0 == nume.isbig) && (0 == deno.isbig))
00651     {
00652       quotient.num = sgn * nume.lo;
00653       quotient.denom = deno.lo;
00654       goto dive_done;
00655     }
00656     else if (0 == deno.isbig)
00657     {
00658       quotient = reduce128 (nume, deno.lo);
00659       if (0 == gnc_numeric_check (quotient))
00660       {
00661         quotient.num *= sgn;
00662         goto dive_done;
00663       }
00664     }
00665 
00666     /* If rounding allowed, then shift until there's no 
00667      * more overflow. The conversion at the end will fix 
00668      * things up for the final value. */
00669     if ((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER)
00670     {
00671       return gnc_numeric_error (GNC_ERROR_OVERFLOW);
00672     }
00673     while (nume.isbig || deno.isbig)
00674     {
00675        nume = shift128 (nume);
00676        deno = shift128 (deno);
00677     }
00678     quotient.num = sgn * nume.lo;
00679     quotient.denom = deno.lo;
00680     if (0 == quotient.denom) 
00681     {
00682       return gnc_numeric_error (GNC_ERROR_OVERFLOW);
00683     }
00684   }
00685   
00686   if(quotient.denom < 0) 
00687   {
00688     quotient.num   = -quotient.num;
00689     quotient.denom = -quotient.denom;
00690   }
00691   
00692 dive_done:
00693   if((denom == GNC_DENOM_AUTO) &&
00694      ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) 
00695   {
00696     denom = gnc_numeric_lcd(a, b);
00697     how   = how & GNC_NUMERIC_RND_MASK;
00698   }
00699 
00700   return gnc_numeric_convert(quotient, denom, how); 
00701 }

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 1191 of file gnc-numeric.c.

01194 {
01195   gnc_numeric quot  = gnc_numeric_div(a, b, denom, how);
01196   gnc_numeric exact = gnc_numeric_div(a, b, GNC_DENOM_AUTO, 
01197                                       GNC_HOW_DENOM_REDUCE);
01198   gnc_numeric err   = gnc_numeric_sub(quot, exact, 
01199                                       GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);
01200   if(error) {
01201     *error = err;
01202   }
012