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.
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 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 | ) | (- ( 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 | ) | ( ((( 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 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.
| 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:
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.
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
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;
| 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