diff --git a/src/engine/gnc-numeric.c b/src/engine/gnc-numeric.c index a76c95ca83..9d3e77b283 100644 --- a/src/engine/gnc-numeric.c +++ b/src/engine/gnc-numeric.c @@ -34,11 +34,6 @@ #include "gnc-numeric.h" -/* TODO - * - use longer intermediate values to make operations - * 64-bit-overflow-proof - */ - /* static short module = MOD_ENGINE; */ /* =============================================================== */ @@ -365,8 +360,8 @@ static const char * _numeric_error_strings[] = "No error", "Argument is not a valid number", "Intermediate result overflow", - "Argument denominators differ in GNC_DENOM_FIXED operation", - "Remainder part in GNC_RND_NEVER operation" + "Argument denominators differ in GNC_HOW_DENOM_FIXED operation", + "Remainder part in GNC_HOW_RND_NEVER operation" }; #endif @@ -606,7 +601,7 @@ gnc_numeric_add(gnc_numeric a, gnc_numeric b, } if((denom == GNC_DENOM_AUTO) && - (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) + (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if(a.denom == b.denom) { denom = a.denom; @@ -668,7 +663,7 @@ gnc_numeric_add(gnc_numeric a, gnc_numeric b, } if((denom == GNC_DENOM_AUTO) && - ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; @@ -711,7 +706,7 @@ gnc_numeric_mul(gnc_numeric a, gnc_numeric b, } if((denom == GNC_DENOM_AUTO) && - (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) { + (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if(a.denom == b.denom) { denom = a.denom; } @@ -758,7 +753,7 @@ gnc_numeric_mul(gnc_numeric a, gnc_numeric b, #endif if((denom == GNC_DENOM_AUTO) && - ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; @@ -785,7 +780,7 @@ gnc_numeric_div(gnc_numeric a, gnc_numeric b, } if((denom == GNC_DENOM_AUTO) && - (how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_FIXED) + (how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_FIXED) { if(a.denom == b.denom) { @@ -880,7 +875,7 @@ gnc_numeric_div(gnc_numeric a, gnc_numeric b, } if((denom == GNC_DENOM_AUTO) && - ((how & GNC_NUMERIC_DENOM_MASK) == GNC_DENOM_LCD)) + ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_LCD)) { denom = gnc_numeric_lcd(a, b); how = how & GNC_NUMERIC_RND_MASK; @@ -942,17 +937,17 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) switch(how & GNC_NUMERIC_DENOM_MASK) { default: - case GNC_DENOM_LCD: /* LCD is meaningless with AUTO in here */ - case GNC_DENOM_EXACT: + case GNC_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */ + case GNC_HOW_DENOM_EXACT: return in; break; - case GNC_DENOM_REDUCE: + case GNC_HOW_DENOM_REDUCE: /* reduce the input to a relatively-prime fraction */ return gnc_numeric_reduce(in); break; - case GNC_DENOM_FIXED: + case GNC_HOW_DENOM_FIXED: if(in.denom != denom) { return gnc_numeric_error(GNC_ERROR_DENOM_DIFF); } @@ -961,7 +956,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) } break; - case GNC_DENOM_SIGFIG: + case GNC_HOW_DENOM_SIGFIG: ratio = fabs(gnc_numeric_to_double(in)); if(ratio < 10e-20) { logratio = 0; @@ -971,7 +966,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) logratio = ((logratio > 0.0) ? (floor(logratio)+1.0) : (ceil(logratio))); } - sigfigs = GNC_NUMERIC_GET_SIGFIGS(how); + sigfigs = GNC_HOW_GET_SIGFIGS(how); if(sigfigs-logratio >= 0) { denom = (gint64)(pow(10, sigfigs-logratio)); @@ -980,7 +975,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) denom = -((gint64)(pow(10, logratio-sigfigs))); } - how = how & ~GNC_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; + how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; break; } @@ -1030,26 +1025,26 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) if(remainder > 0) { switch(how) { - case GNC_RND_FLOOR: + case GNC_HOW_RND_FLOOR: if(sign < 0) { out.num = out.num + 1; } break; - case GNC_RND_CEIL: + case GNC_HOW_RND_CEIL: if(sign > 0) { out.num = out.num + 1; } break; - case GNC_RND_TRUNC: + case GNC_HOW_RND_TRUNC: break; - case GNC_RND_PROMOTE: + case GNC_HOW_RND_PROMOTE: out.num = out.num + 1; break; - case GNC_RND_ROUND_HALF_DOWN: + case GNC_HOW_RND_ROUND_HALF_DOWN: if(denom_neg) { if((2 * remainder) > in.denom*denom) { out.num = out.num + 1; @@ -1060,7 +1055,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) } break; - case GNC_RND_ROUND_HALF_UP: + case GNC_HOW_RND_ROUND_HALF_UP: if(denom_neg) { if((2 * remainder) >= in.denom*denom) { out.num = out.num + 1; @@ -1071,7 +1066,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) } break; - case GNC_RND_ROUND: + case GNC_HOW_RND_ROUND: if(denom_neg) { if((2 * remainder) > in.denom*denom) { out.num = out.num + 1; @@ -1094,7 +1089,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) } break; - case GNC_RND_NEVER: + case GNC_HOW_RND_NEVER: return gnc_numeric_error(GNC_ERROR_REMAINDER); break; } @@ -1108,7 +1103,7 @@ gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how) /******************************************************************** ** reduce a fraction by GCF elimination. This is NOT done as a - * part of the arithmetic API unless GNC_DENOM_REDUCE is specified + * part of the arithmetic API unless GNC_HOW_DENOM_REDUCE is specified * as the output denominator. ********************************************************************/ @@ -1154,7 +1149,8 @@ double_to_gnc_numeric(double in, gint64 denom, gint how) double logval; double sigfigs; - if((denom == GNC_DENOM_AUTO) && (how & GNC_DENOM_SIGFIG)) { + if((denom == GNC_DENOM_AUTO) && (how & GNC_HOW_DENOM_SIGFIG)) + { if(fabs(in) < 10e-20) { logval = 0; } @@ -1163,7 +1159,7 @@ double_to_gnc_numeric(double in, gint64 denom, gint how) logval = ((logval > 0.0) ? (floor(logval)+1.0) : (ceil(logval))); } - sigfigs = GNC_NUMERIC_GET_SIGFIGS(how); + sigfigs = GNC_HOW_GET_SIGFIGS(how); if(sigfigs-logval >= 0) { denom = (gint64)(pow(10, sigfigs-logval)); } @@ -1171,7 +1167,7 @@ double_to_gnc_numeric(double in, gint64 denom, gint how) denom = -((gint64)(pow(10, logval-sigfigs))); } - how = how & ~GNC_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; + how = how & ~GNC_HOW_DENOM_SIGFIG & ~GNC_NUMERIC_SIGFIGS_MASK; } int_part = (gint64)(floor(fabs(in))); @@ -1181,24 +1177,24 @@ double_to_gnc_numeric(double in, gint64 denom, gint how) frac_part = frac_part * (double)denom; switch(how & GNC_NUMERIC_RND_MASK) { - case GNC_RND_FLOOR: + case GNC_HOW_RND_FLOOR: frac_int = (gint64)floor(frac_part); break; - case GNC_RND_CEIL: + case GNC_HOW_RND_CEIL: frac_int = (gint64)ceil(frac_part); break; - case GNC_RND_TRUNC: + case GNC_HOW_RND_TRUNC: frac_int = (gint64)frac_part; break; - case GNC_RND_ROUND: - case GNC_RND_ROUND_HALF_UP: + case GNC_HOW_RND_ROUND: + case GNC_HOW_RND_ROUND_HALF_UP: frac_int = (gint64)rint(frac_part); break; - case GNC_RND_NEVER: + case GNC_HOW_RND_NEVER: frac_int = (gint64)floor(frac_part); if(frac_part != (double) frac_int) { /* signal an error */ @@ -1226,21 +1222,6 @@ gnc_numeric_to_double(gnc_numeric in) } } - -/******************************************************************** - * gnc_numeric_create - ********************************************************************/ - -gnc_numeric -gnc_numeric_create(gint64 num, gint64 denom) -{ - gnc_numeric out; - out.num = num; - out.denom = denom; - return out; -} - - /******************************************************************** * gnc_numeric_error ********************************************************************/ @@ -1252,39 +1233,6 @@ gnc_numeric_error(GNCNumericErrorCode error_code) } -/******************************************************************** - * gnc_numeric_zero - ********************************************************************/ - -gnc_numeric -gnc_numeric_zero(void) -{ - return gnc_numeric_create(0LL, 1LL); -} - - -/******************************************************************** - * gnc_numeric_num - ********************************************************************/ - -gint64 -gnc_numeric_num(gnc_numeric a) -{ - return a.num; -} - - -/******************************************************************** - * gnc_numeric_denom - ********************************************************************/ - -gint64 -gnc_numeric_denom(gnc_numeric a) -{ - return a.denom; -} - - /******************************************************************** * gnc_numeric_add_with_error ********************************************************************/ @@ -1297,9 +1245,9 @@ gnc_numeric_add_with_error(gnc_numeric a, gnc_numeric b, gnc_numeric sum = gnc_numeric_add(a, b, denom, how); gnc_numeric exact = gnc_numeric_add(a, b, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(sum, exact, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); if(error) { *error = err; @@ -1318,9 +1266,9 @@ gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, { gnc_numeric diff = gnc_numeric_sub(a, b, denom, how); gnc_numeric exact = gnc_numeric_sub(a, b, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(diff, exact, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); if(error) { *error = err; } @@ -1339,9 +1287,9 @@ gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, { gnc_numeric prod = gnc_numeric_mul(a, b, denom, how); gnc_numeric exact = gnc_numeric_mul(a, b, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(prod, exact, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); if(error) { *error = err; } @@ -1360,9 +1308,9 @@ gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, { gnc_numeric quot = gnc_numeric_div(a, b, denom, how); gnc_numeric exact = gnc_numeric_div(a, b, GNC_DENOM_AUTO, - GNC_DENOM_REDUCE); + GNC_HOW_DENOM_REDUCE); gnc_numeric err = gnc_numeric_sub(quot, exact, - GNC_DENOM_AUTO, GNC_DENOM_REDUCE); + GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); if(error) { *error = err; } @@ -1444,25 +1392,25 @@ main(int argc, char ** argv) gnc_numeric err; - c = gnc_numeric_add_with_error(a, b, 100, GNC_RND_ROUND, &err); + c = gnc_numeric_add_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("add 100ths/error : %s + %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); - c = gnc_numeric_sub_with_error(a, b, 100, GNC_RND_FLOOR, &err); + c = gnc_numeric_sub_with_error(a, b, 100, GNC_HOW_RND_FLOOR, &err); printf("sub 100ths/error : %s - %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); - c = gnc_numeric_mul_with_error(a, b, 100, GNC_RND_ROUND, &err); + c = gnc_numeric_mul_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("mul 100ths/error : %s * %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), gnc_numeric_print(err)); - c = gnc_numeric_div_with_error(a, b, 100, GNC_RND_ROUND, &err); + c = gnc_numeric_div_with_error(a, b, 100, GNC_HOW_RND_ROUND, &err); printf("div 100ths/error : %s / %s = %s + (error) %s\n\n", gnc_numeric_print(a), gnc_numeric_print(b), gnc_numeric_print(c), @@ -1470,11 +1418,11 @@ main(int argc, char ** argv) printf("multiply (EXACT): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), - gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_DENOM_EXACT))); + gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT))); printf("multiply (REDUCE): %s * %s = %s\n", gnc_numeric_print(a), gnc_numeric_print(b), - gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_DENOM_REDUCE))); + gnc_numeric_print(gnc_numeric_mul(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE))); return 0; diff --git a/src/engine/gnc-numeric.h b/src/engine/gnc-numeric.h index 3dc48d7062..a7ed4fff03 100644 --- a/src/engine/gnc-numeric.h +++ b/src/engine/gnc-numeric.h @@ -27,7 +27,7 @@ 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. + 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 @@ -42,204 +42,10 @@ @author Copyright (C) 2004 Linas Vepstas */ -#if JUNK - -/******************* -@menu -* Standard Numeric Arguments:: -* Creating Numeric Objects:: -* Basic Arithmetic Operations:: -* Numeric Comparisons and Predicates:: -* Numeric Denominator Conversion:: -* Numeric Floating Point Conversion:: -* Numeric String Conversion:: -* Numeric Error Handling :: -* Numeric Example:: -@end menu - -@node Standard Numeric Arguments, Creating Numeric Objects, Numeric Library, Numeric Library -@subsection Standard Numeric Arguments -@cindex Standard Numeric Arguments - -It is useful to specify a denominator in cases where it is known that -the output value is of constrained precision. For example, monetary -transactions must be executed in an integer number of the "smallest -currency unit" of the transaction. In US Dollars, the smallest currency -unit is the cent, and all monetary transactions must be done in units of -cents. Therefore, any fractional cents in a computed price must be -rounded away. - -Most of the @code{gnc_numeric} arithmetic functions take two arguments -in addition to their numeric args: @var{denom}, which is the denominator -to use in the output @code{gnc_numeric object}, and @var{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 exact computations to be -properly rounded to the appropriate units. - -Valid values for @var{denom} are: - -Valid values for @var{how} are bitwise combinations of zero or one -"rounding instructions" with zero or one "denominator types". - -The denominator type specifies how to compute a denominator if -@code{GNC_DENOM_AUTO} is specified as the @var{denom}. Valid denominator -types are: - - - -To use traditional rational-number operational semantics (all results -are exact and are reduced to relatively-prime fractions) pass the -argument @code{GNC_DENOM_AUTO} as @var{denom} and @code{GNC_DENOM_REDUCE -| GNC_RND_NEVER} as @var{how}. - -To enforce strict financial semantics (such that all operands must have -the same denominator as each other and as the result), use -@var{GNC_DENOM_AUTO} as @var{denom} and @code{GNC_DENOM_FIXED | -GNC_RND_NEVER} as @var{how}. - - -@node Creating Numeric Objects, Basic Arithmetic Operations, Standard Numeric Arguments, Numeric Library -@subsection Creating Numeric Objects -@cindex Creating Numeric Objects - -@deftypefun gnc_numeric gnc_numeric_create (int @var{num}, int @var{denom}) -Create a @code{gnc_numeric} object with a value of "@var{num} / @var{denom}". -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_zero () -Create a @code{gnc_numeric} object with a value of 0. -@end deftypefun - - -@node Basic Arithmetic Operations, Numeric Comparisons and Predicates, Creating Numeric Objects, Numeric Library -@subsection Basic Arithmetic Operations -@cindex Basic Arithmetic Operations - -See @ref{Standard Numeric Arguments} for a description of the @var{denom} -and @var{how} arguments to each arithmetic function. - -@deftypefun gnc_numeric gnc_numeric_add (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}) -Return the sum of @var{a} and @var{b}. -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_sub (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}) -Return "@var{a} - @var{b}". -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_div (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}) -Return "@var{a} / @var{b}". -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_add_with_error (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}, {gnc_numeric *} @var{error}) -The same as @code{gnc_numeric_add}, but uses @var{error} for accumulating -conversion roundoff error. -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_sub_with_error (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}, {gnc_numeric *} @var{error}) -The same as @code{gnc_numeric_sub}, but uses @var{error} for accumulating -conversion roundoff error. -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_div_with_error (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}, {gnc_numeric *} @var{error}) -The same as @code{gnc_numeric_div}, but uses @var{error} for accumulating -conversion roundoff error. -@end deftypefun - - -@node Numeric Comparisons and Predicates, Numeric Denominator Conversion, Basic Arithmetic Operations, Numeric Library -@subsection Numeric Comparisons and Predicates -@cindex Numeric Comparisons and Predicates - -@deftypefun int gnc_numeric_compare (gnc_numeric @var{a}, gnc_numeric @var{b}) -Returns +1 if @code{@var{a} > @var{b}}, -1 if @code{@var{b} > @var{a}}, 0 if @code{@var{a} == @var{b}}. -@end deftypefun - -@deftypefun int gnc_numeric_eq (gnc_numeric @var{a}, gnc_numeric @var{b}) -Returns 1 if @code{numerator(@var{a}) == numerator(@var{b})} and -@code{denominator(@var{a}) == denominator(@var{b})}, otherwise returns 0. -@end deftypefun - -@deftypefun int gnc_numeric_equal (gnc_numeric @var{a}, gnc_numeric @var{b}) -Returns 1 if the fraction represented by @var{a} is equal to the fraction -represented by @var{b}, otherwise returns 0. -@end deftypefun - -@deftypefun int gnc_numeric_same (gnc_numeric @var{a}, gnc_numeric @var{b}, gint64 @var{denom}, gint @var{how}) -Convert both @var{a} and @var{b} to @var{denom} (@pxref{Standard Numeric -Arguments} and compare numerators of the result. - -@example - For example, if @code{@var{a} == 7/16} and @code{@var{b} == 3/4}, - @code{gnc_numeric_same(@var{a}, @var{b}, 2, GNC_RND_TRUNC) == 1} - because both 7/16 and 3/4 round to 1/2 under truncation. However, - @code{gnc_numeric_same(@var{a}, @var{b}, 2, GNC_RND_ROUND) == 0} - because 7/16 rounds to 1/2 under unbiased rounding but 3/4 rounds - to 2/2. -@end example -@end deftypefun - - -@node Numeric Denominator Conversion, Numeric Floating Point Conversion, Numeric Comparisons and Predicates, Numeric Library -@subsection Numeric Denominator Conversion -@cindex Numeric Denominator Conversion - -@deftypefun gnc_numeric gnc_numeric_convert (gnc_numeric @var{in}, gint64 @var{denom}, gint @var{how}) -Convert @var{in} to the specified denominator under standard arguments -@var{denom} and @var{how}. @xref{Standard Numeric Arguments}. -@end deftypefun - -@deftypefun gnc_numeric gnc_numeric_convert_with_error (gnc_numeric @var{in}, gint64 @var{denom}, gint @var{how}, {gnc_numeric *} @var{error}) -Same as @code{gnc_numeric_convert}, but return a remainder value for -accumulating conversion error. -@end deftypefun - -@node Numeric Floating Point Conversion, Numeric String Conversion, Numeric Denominator Conversion, Numeric Library -@subsection Numeric Floating Point Conversion -@cindex Numeric Floating Point Conversion - -@deftypefun gnc_numeric double_to_gnc_numeric (double @var{arg}, gint64 @var{denom}, gint @var{how}) -Convert a floating-point number to a @code{gnc_numeric}. Both @var{denom} -and @var{how} are used as in arithmetic, but @code{GNC_DENOM_AUTO} is -not recognized. -@end deftypefun - -@deftypefun double gnc_numeric_to_double (gnc_numeric @var{arg}) -Convert @var{arg} to a @code{double} value. -@end deftypefun - - -@node Numeric String Conversion, Numeric Error Handling , Numeric Floating Point Conversion, Numeric Library -@subsection Numeric String Conversion -@cindex Numeric String Conversion - -@deftypefun {gchar *} gnc_numeric_to_string (gnc_numeric @var{n}) -Return a string representation of @var{n}. The string must be -freed with @code{g_free}. -@end deftypefun - -@deftypefun {const gchar *} string_to_gnc_numeric (const {gchar *} @var{str}, {gnc_numeric *} @var{n}) -Read a @code{gnc_numeric} from @var{str}, skipping any leading -whitespace, and returning a pointer to just past the last byte -read. Return NULL on error. -@end deftypefun - - -@node Numeric Error Handling , Numeric Example, Numeric String Conversion, Numeric Library -@subsection Numeric Error Handling -@cindex Numeric Error Handling - - -@deftypefun gnc_numeric gnc_numeric_error (int error_code) -Create a @code{gnc_numeric} object that signals the error condition -noted by @var{error_code} rather than a number. -@end deftypefun - - -@node Numeric Example, , Numeric Error Handling , Numeric Library -@subsection Numeric Example -@cindex Numeric Example +/** ---------------------------------------------------------- +EXAMPLE +------- The following program finds the best @code{gnc_numeric} approximation to the @file{math.h} constant @code{M_PI} given a maximum denominator. For large denominators, the @code{gnc_numeric} approximation is accurate to @@ -260,7 +66,7 @@ this may not be good enough. For example, int main(int argc, char ** argv) -@{ +{ gnc_numeric approx, best; double err, best_err=1.0; double m_pi = M_PI; @@ -270,43 +76,90 @@ main(int argc, char ** argv) sscanf(argv[1], "%Ld", &max); for (denom = 1; denom < max; denom++) - @{ + { approx = double_to_gnc_numeric (m_pi, denom, GNC_RND_ROUND); err = m_pi - gnc_numeric_to_double (approx); if (fabs (err) < fabs (best_err)) - @{ + { best = approx; best_err = err; printf ("%Ld / %Ld = %.30f\n", gnc_numeric_num (best), gnc_numeric_denom (best), gnc_numeric_to_double (best)); - @} - @} -@} -@end example - -**********************/ -#endif + } + } +} +----------------------------------------------------------------- */ #ifndef GNC_NUMERIC_H #define GNC_NUMERIC_H #include -struct _gnc_numeric { +struct _gnc_numeric +{ gint64 num; gint64 denom; }; -/** @brief An exact-number type +/** @brief An rational-number type * - * This is a rational number, defined by nominator and denominator. */ + * This is a rational number, defined by numerator and denominator. */ typedef struct _gnc_numeric gnc_numeric; +/** @name Arguments + * @brief Standard Arguments to most functions + + Most of the gnc_numeric arithmetic functions take two arguments + in addition to their numeric args: @var{denom}, which is the denominator + to use in the output @code{gnc_numeric object}, and @var{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 @var{denom} are: + GNC_DENOM_AUTO -- compute denominator exactly + integer n -- Force the denominator of teh result to be this integer + GNC_DENOM_RECIPROCAL -- Use 1/n as the denominator (???huh???) + + Valid values for @var{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 + @code{GNC_DENOM_AUTO} is specified as the @var{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 @code{GNC_DENOM_AUTO} as @var{denom} and + @code{GNC_HOW_DENOM_REDUCE| GNC_HOW_RND_NEVER} as @var{how}. + + To enforce strict financial semantics (such that all operands must have + the same denominator as each other and as the result), use + @var{GNC_DENOM_AUTO} as @var{denom} and + @code{GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER} as @var{how}. + + @{ */ + /** bitmasks for HOW flags. * bits 8-15 of 'how' are reserved for the number of significant - * digits to use in the output with GNC_DENOM_SIGFIG */ - + * digits to use in the output with GNC_HOW_DENOM_SIGFIG + */ #define GNC_NUMERIC_RND_MASK 0x0000000f #define GNC_NUMERIC_DENOM_MASK 0x000000f0 #define GNC_NUMERIC_SIGFIGS_MASK 0x0000ff00 @@ -321,38 +174,38 @@ typedef struct _gnc_numeric gnc_numeric; */ enum { /** Round toward -infinity */ - GNC_RND_FLOOR = 0x01, + GNC_HOW_RND_FLOOR = 0x01, /** Round toward +infinity */ - GNC_RND_CEIL = 0x02, + GNC_HOW_RND_CEIL = 0x02, /** Truncate fractions (round toward zero) */ - GNC_RND_TRUNC = 0x03, + GNC_HOW_RND_TRUNC = 0x03, /** Promote fractions (round away from zero) */ - GNC_RND_PROMOTE = 0x04, + GNC_HOW_RND_PROMOTE = 0x04, /** Round to the nearest integer, rounding toward zero * when there are two equidistant nearest integers. */ - GNC_RND_ROUND_HALF_DOWN = 0x05, + GNC_HOW_RND_ROUND_HALF_DOWN = 0x05, /** Round to the nearest integer, rounding away from zero * when there are two equidistant nearest integers. */ - GNC_RND_ROUND_HALF_UP = 0x06, + GNC_HOW_RND_ROUND_HALF_UP = 0x06, /** 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_RND_ROUND = 0x07, + GNC_HOW_RND_ROUND = 0x07, /** Never round at all, and signal an error if there is a * fractional result in a computation. */ - GNC_RND_NEVER = 0x08 + GNC_HOW_RND_NEVER = 0x08 }; /** How to compute a denominator, or'ed into the "how" field. */ @@ -362,42 +215,48 @@ enum { * lose any information in the result but also do not want to * spend any time finding the "best" denominator. */ - GNC_DENOM_EXACT = 0x10, + GNC_HOW_DENOM_EXACT = 0x10, /** 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_DENOM_REDUCE = 0x20, + GNC_HOW_DENOM_REDUCE = 0x20, /** Find the least common multiple of the arguments' denominators * and use that as the denominator of the result. */ - GNC_DENOM_LCD = 0x30, + GNC_HOW_DENOM_LCD = 0x30, /** 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_DENOM_FIXED = 0x40, + GNC_HOW_DENOM_FIXED = 0x40, /** Round to the number of significant figures given in the rounding - * instructions by the GNC_DENOM_SIGFIGS () macro. + * instructions by the GNC_HOW_DENOM_SIGFIGS () macro. */ - GNC_DENOM_SIGFIG = 0x50 + GNC_HOW_DENOM_SIGFIG = 0x50 }; +/** Build a 'how' value that will generate a denominator that will + * keep at least n significant figures in the result. + */ +#define GNC_HOW_DENOM_SIGFIGS( n ) ( ((( n ) & 0xff) << 8) | GNC_HOW_DENOM_SIGFIG) +#define GNC_HOW_GET_SIGFIGS( a ) ( (( a ) & 0xff00 ) >> 8) + /** Error codes */ typedef enum { GNC_ERROR_OK = 0, /**< No error */ GNC_ERROR_ARG = -1, /**< Argument is not a valid number */ GNC_ERROR_OVERFLOW = -2, /**< Intermediate result overflow */ - /** GNC_DENOM_FIXED was specified, but argument denominators differed. */ + /** GNC_HOW_DENOM_FIXED was specified, but argument denominators differed. */ GNC_ERROR_DENOM_DIFF = -3, - /** GNC_RND_NEVER was specified, but the result could not be + /** GNC_HOW_RND_NEVER was specified, but the result could not be * converted to the desired denominator without a remainder. */ GNC_ERROR_REMAINDER = -4 } GNCNumericErrorCode; @@ -417,50 +276,61 @@ typedef enum { /** Use the value @code{1/n} as the denominator of the output value. */ #define GNC_DENOM_RECIPROCAL( a ) (- ( a )) -/** Use a value for the denominator that will keep at least n - * significant figures in the result. - */ -#define GNC_DENOM_SIGFIGS( n ) ( ((( n ) & 0xff) << 8) | GNC_DENOM_SIGFIG) -#define GNC_NUMERIC_GET_SIGFIGS( a ) ( (( a ) & 0xff00 ) >> 8) +/** @} */ -/** @name Constructors */ -/*@{*/ -/** make a gnc_numeric from numerator and denominator */ -gnc_numeric gnc_numeric_create(gint64 num, gint64 denom); +/** @name Constructors + @{*/ +/** Make a gnc_numeric from numerator and denominator */ +static inline +gnc_numeric gnc_numeric_create(gint64 num, gint64 denom) { + gnc_numeric out; + out.num = num; + out.denom = denom; + return out; +} /** create a zero-value gnc_numeric */ -gnc_numeric gnc_numeric_zero(void); +static inline +gnc_numeric gnc_numeric_zero(void) { return gnc_numeric_create(0, 1); } -/** convert from floating-point values */ +/** Convert a floating-point number to a gnc_numeric. + * Both @var{denom} and @var{how} are used as in arithmetic, + * but @code{GNC_DENOM_AUTO} is not recognized; a denominator + * must be specified either explicitctly or through sigfigs. + */ gnc_numeric double_to_gnc_numeric(double in, gint64 denom, gint how); -/** Read a gnc_numeric from str, skipping any leading whitespace, and - returning a pointer to just past the last byte read. Return NULL - on error. */ +/** Read a gnc_numeric from str, skipping any leading whitespace, + * and return a pointer to just past the last byte read. + * Return NULL on error. */ const gchar *string_to_gnc_numeric(const gchar* str, gnc_numeric *n); -/** make a special error-signalling gnc_numeric */ +/** Create a gnc_numeric object that signals the error condition + * noted by @var{error_code}, rather than a number. + */ gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code); /*@}*/ /** @name Value accessors */ -/*@{*/ -/** Get parts */ -gint64 gnc_numeric_num(gnc_numeric a); -/** Get parts */ -gint64 gnc_numeric_denom(gnc_numeric a); +/** @{*/ +/** Return numerator */ +static inline +gint64 gnc_numeric_num(gnc_numeric a) { return a.num; } +/** Return denominator */ +static inline +gint64 gnc_numeric_denom(gnc_numeric a) { return a.denom; } -/** Convert to floating-point values */ +/** Convert numeric to floating-point value. */ double gnc_numeric_to_double(gnc_numeric in); /** Convert to string. The returned buffer is to be g_free'd by the - * caller (it was allocated through g_strdup) */ + * caller (it was allocated through g_strdup) */ gchar *gnc_numeric_to_string(gnc_numeric n); /*@}*/ /** @name Tests */ -/*@{*/ +/** @{*/ /** 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. @@ -479,29 +349,42 @@ gboolean gnc_numeric_negative_p(gnc_numeric a); /** Returns 1 if @var{a} > 0, otherwise returns 0. */ gboolean gnc_numeric_positive_p(gnc_numeric a); -/** Equivalence predicate: Returns TRUE (1) if a and b are exactly the - * same (same numerator and denominator) +/** Equivalence predicate: Returns TRUE (1) if a and b are + * exactly the same (have the same numerator and denominator) */ gboolean gnc_numeric_eq(gnc_numeric a, gnc_numeric b); /** Equivalence predicate: Returns TRUE (1) if a and b represent - * exactly the same number (ratio of numerator to denominator is - * exactly equal) + * the same number. That is, return TRUE if the ratios, when + * reduced by eliminating common factors, are identical. */ gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b); -/** Equivalence predicate: Returns TRUE (1) if after both are - * converted to DENOM using method HOW, a and b are - * gnc_numeric_equal(). +/** Equivalence predicate: + * Convert both @var{a} and @var{b} to @var{denom} using the + * specified DENOM and method HOW, and compare numerators + * the results using gnc_numeric_equal. + * +@example + For example, if @code{@var{a} == 7/16} and @code{@var{b} == 3/4}, + @code{gnc_numeric_same(@var{a}, @var{b}, 2, GNC_HOW_RND_TRUNC) == 1} + because both 7/16 and 3/4 round to 1/2 under truncation. However, + @code{gnc_numeric_same(@var{a}, @var{b}, 2, GNC_HOW_RND_ROUND) == 0} + because 7/16 rounds to 1/2 under unbiased rounding but 3/4 rounds + to 2/2. +@end example */ int gnc_numeric_same(gnc_numeric a, gnc_numeric b, gint64 denom, gint how); /*@}*/ /** @name Arithmetic operations */ -/*@{*/ +/** @{*/ +/** Return a+b. */ gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how); + +/** Return a-b. */ gnc_numeric gnc_numeric_sub(gnc_numeric a, gnc_numeric b, gint64 denom, gint how); @@ -530,30 +413,35 @@ gnc_numeric gnc_numeric_abs(gnc_numeric a); /** * Shortcut for common case: gnc_numeric_add(a, b, GNC_DENOM_AUTO, - * GNC_DENOM_FIXED | GNC_RND_NEVER); + * GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER); */ static inline gnc_numeric gnc_numeric_add_fixed(gnc_numeric a, gnc_numeric b) { return gnc_numeric_add(a, b, GNC_DENOM_AUTO, - GNC_DENOM_FIXED | GNC_RND_NEVER); + GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER); } /** * Shortcut for most common case: gnc_numeric_sub(a, b, GNC_DENOM_AUTO, - * GNC_DENOM_FIXED | GNC_RND_NEVER); + * GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER); */ static inline gnc_numeric gnc_numeric_sub_fixed(gnc_numeric a, gnc_numeric b) { return gnc_numeric_sub(a, b, GNC_DENOM_AUTO, - GNC_DENOM_FIXED | GNC_RND_NEVER); + GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER); } /*@}*/ /** @name Arithmetic functions with exact error returns */ -/*@{*/ +/** @{*/ +/** The same as gnc_numeric_add, but uses @var{error} for accumulating + * conversion roundoff error. */ 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_sub, but uses @var{error} for accumulating + * conversion roundoff error. */ gnc_numeric gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric * error); @@ -564,18 +452,27 @@ gnc_numeric gnc_numeric_sub_with_error(gnc_numeric a, gnc_numeric b, gnc_numeric gnc_numeric_mul_with_error(gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric * error); + +/** The same as @code{gnc_numeric_div}, but uses @var{error} for + * accumulating conversion roundoff error. + */ gnc_numeric gnc_numeric_div_with_error(gnc_numeric a, gnc_numeric b, gint64 denom, gint how, gnc_numeric * error); /*@}*/ /** @name Change denominator */ -/*@{*/ -/** change the denominator of a gnc_numeric value */ +/** @{*/ +/** Change the denominator of a gnc_numeric value to the + * specified denominator under standard arguments + * @var{denom} and @var{how}. + */ gnc_numeric gnc_numeric_convert(gnc_numeric in, gint64 denom, gint how); -/** change the denominator of a gnc_numeric value */ +/** Same as @code{gnc_numeric_convert}, but return a remainder + * value for accumulating conversion error. +*/ gnc_numeric gnc_numeric_convert_with_error(gnc_numeric in, gint64 denom, gint how, gnc_numeric * error); @@ -585,7 +482,22 @@ gnc_numeric gnc_numeric_convert_with_error(gnc_numeric in, gint64 denom, gnc_numeric gnc_numeric_reduce(gnc_numeric in); /*@}*/ +/** Deprecated, backwards-compate 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) #endif - -/*@}*/