Data Structures | |
struct | _QofNumeric |
Modules | |
Math128 | |
Files | |
file | qofnumeric.h |
An exact-rational-number library for QOF. | |
Typedefs | |
typedef struct _QofNumeric | QofNumeric |
A rational-number type. |
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.
#define QOF_DENOM_AUTO 0 |
Values that can be passed as the 'denom' argument. The include a positive number n to be used as the denominator of the output value. Other possibilities include the list below: Compute an appropriate denominator automatically. Flags in the 'how' argument will specify how to compute the denominator.
Definition at line 231 of file qofnumeric.h.
#define QOF_DENOM_RECIPROCAL | ( | a | ) | (- ( a )) |
Use the value 1/n as the denominator of the output value.
Definition at line 234 of file qofnumeric.h.
#define QOF_HOW_DENOM_SIGFIGS | ( | n | ) | ( ((( n ) & 0xff) << 8) | QOF_HOW_DENOM_SIGFIG) |
Build a 'how' value that will generate a denominator that will keep at least n significant figures in the result.
Definition at line 203 of file qofnumeric.h.
#define QOF_NUMERIC_RND_MASK 0x0000000f |
bitmasks for HOW flags.
bits 8-15 of 'how' are reserved for the number of significant digits to use in the output with QOF_HOW_DENOM_SIGFIG
Definition at line 116 of file qofnumeric.h.
typedef struct _QofNumeric QofNumeric |
A rational-number type.
This is a rational number, defined by numerator and denominator.
Definition at line 61 of file qofnumeric.h.
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 129 of file qofnumeric.h.
00130 { 00132 QOF_HOW_RND_FLOOR = 0x01, 00133 00135 QOF_HOW_RND_CEIL = 0x02, 00136 00138 QOF_HOW_RND_TRUNC = 0x03, 00139 00141 QOF_HOW_RND_PROMOTE = 0x04, 00142 00146 QOF_HOW_RND_ROUND_HALF_DOWN = 0x05, 00147 00151 QOF_HOW_RND_ROUND_HALF_UP = 0x06, 00152 00158 QOF_HOW_RND_ROUND = 0x07, 00159 00163 QOF_HOW_RND_NEVER = 0x08 00164 };
anonymous enum |
How to compute a denominator, or'ed into the "how" field.
Definition at line 167 of file qofnumeric.h.
00168 { 00174 QOF_HOW_DENOM_EXACT = 0x10, 00175 00181 QOF_HOW_DENOM_REDUCE = 0x20, 00182 00186 QOF_HOW_DENOM_LCD = 0x30, 00187 00192 QOF_HOW_DENOM_FIXED = 0x40, 00193 00197 QOF_HOW_DENOM_SIGFIG = 0x50 00198 };
enum QofNumericErrorCode |
Error codes
Definition at line 207 of file qofnumeric.h.
00208 { 00209 QOF_ERROR_OK = 0, 00210 QOF_ERROR_ARG = -1, 00211 QOF_ERROR_OVERFLOW = -2, 00214 QOF_ERROR_DENOM_DIFF = -3, 00215 00218 QOF_ERROR_REMAINDER = -4 00219 } QofNumericErrorCode;
QofNumeric qof_numeric_abs | ( | QofNumeric | a | ) |
Return the absolute value of the argument
Definition at line 650 of file qofnumeric.c.
00651 { 00652 if (qof_numeric_check (a)) 00653 return qof_numeric_error (QOF_ERROR_ARG); 00654 return qof_numeric_create (ABS (a.num), a.denom); 00655 }
QofNumeric qof_numeric_add | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how | |||
) |
Return a+b.
Definition at line 300 of file qofnumeric.c.
00301 { 00302 QofNumeric sum; 00303 00304 if (qof_numeric_check (a) || qof_numeric_check (b)) 00305 return qof_numeric_error (QOF_ERROR_ARG); 00306 00307 if ((denom == QOF_DENOM_AUTO) && 00308 (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED) 00309 { 00310 if (a.denom == b.denom) 00311 denom = a.denom; 00312 else if (b.num == 0) 00313 { 00314 denom = a.denom; 00315 b.denom = a.denom; 00316 } 00317 else if (a.num == 0) 00318 { 00319 denom = b.denom; 00320 a.denom = b.denom; 00321 } 00322 else 00323 return qof_numeric_error (QOF_ERROR_DENOM_DIFF); 00324 } 00325 00326 if (a.denom < 0) 00327 { 00328 a.num *= -a.denom; /* BUG: overflow not handled. */ 00329 a.denom = 1; 00330 } 00331 00332 if (b.denom < 0) 00333 { 00334 b.num *= -b.denom; /* BUG: overflow not handled. */ 00335 b.denom = 1; 00336 } 00337 00338 /* Get an exact answer.. same denominator is the common case. */ 00339 if (a.denom == b.denom) 00340 { 00341 sum.num = a.num + b.num; /* BUG: overflow not handled. */ 00342 sum.denom = a.denom; 00343 } 00344 else 00345 { 00346 /* We want to do this: 00347 * sum.num = a.num*b.denom + b.num*a.denom; 00348 * sum.denom = a.denom*b.denom; 00349 * but the multiply could overflow. 00350 * Computing the LCD minimizes likelihood of overflow 00351 */ 00352 gint64 lcd; 00353 QofInt128 ca, cb, cab; 00354 00355 lcd = qof_numeric_lcd (a, b); 00356 if (QOF_ERROR_ARG == lcd) 00357 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00358 ca = mult128 (a.num, lcd / a.denom); 00359 if (ca.isbig) 00360 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00361 cb = mult128 (b.num, lcd / b.denom); 00362 if (cb.isbig) 00363 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00364 cab = add128 (ca, cb); 00365 if (cab.isbig) 00366 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00367 sum.num = cab.lo; 00368 if (cab.isneg) 00369 sum.num = -sum.num; 00370 sum.denom = lcd; 00371 } 00372 00373 if ((denom == QOF_DENOM_AUTO) && 00374 ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD)) 00375 { 00376 denom = qof_numeric_lcd (a, b); 00377 how = how & QOF_NUMERIC_RND_MASK; 00378 } 00379 00380 return qof_numeric_convert (sum, denom, how); 00381 }
static QofNumeric qof_numeric_add_fixed | ( | QofNumeric | a, | |
QofNumeric | b | |||
) | [inline, static] |
Shortcut for common case: QofNumeric_add(a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER);
Definition at line 411 of file qofnumeric.h.
00412 { 00413 return qof_numeric_add (a, b, QOF_DENOM_AUTO, 00414 QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER); 00415 }
QofNumeric qof_numeric_add_with_error | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how, | |||
QofNumeric * | error | |||
) |
The same as QofNumeric_add, but uses 'error' for accumulating conversion roundoff error.
Definition at line 1014 of file qofnumeric.c.
01016 { 01017 01018 QofNumeric sum = qof_numeric_add (a, b, denom, how); 01019 QofNumeric exact = qof_numeric_add (a, b, QOF_DENOM_AUTO, 01020 QOF_HOW_DENOM_REDUCE); 01021 QofNumeric err = qof_numeric_sub (sum, exact, QOF_DENOM_AUTO, 01022 QOF_HOW_DENOM_REDUCE); 01023 01024 if (error) 01025 *error = err; 01026 return sum; 01027 }
QofNumericErrorCode qof_numeric_check | ( | QofNumeric | a | ) | [inline] |
Check for error signal in value. Returns QOF_ERROR_OK (==0) if the number appears to be valid, otherwise it returns the type of error. Error values always have a denominator of zero.
Definition at line 44 of file qofnumeric.c.
00045 { 00046 if (in.denom != 0) 00047 return QOF_ERROR_OK; 00048 else if (in.num) 00049 { 00050 if ((0 < in.num) || (-4 > in.num)) 00051 in.num = (gint64) QOF_ERROR_OVERFLOW; 00052 return (QofNumericErrorCode) in.num; 00053 } 00054 else 00055 return QOF_ERROR_ARG; 00056 }
gint qof_numeric_compare | ( | QofNumeric | a, | |
QofNumeric | b | |||
) |
Returns 1 if a>b, -1 if b>a, 0 if a == b
Definition at line 174 of file qofnumeric.c.
00175 { 00176 gint64 aa, bb; 00177 QofInt128 l, r; 00178 00179 if (qof_numeric_check (a) || qof_numeric_check (b)) 00180 return 0; 00181 00182 if (a.denom == b.denom) 00183 { 00184 if (a.num == b.num) 00185 return 0; 00186 if (a.num > b.num) 00187 return 1; 00188 return -1; 00189 } 00190 00191 if ((a.denom > 0) && (b.denom > 0)) 00192 { 00193 /* Avoid overflows using 128-bit intermediate math */ 00194 l = mult128 (a.num, b.denom); 00195 r = mult128 (b.num, a.denom); 00196 return cmp128 (l, r); 00197 } 00198 00199 if (a.denom < 0) 00200 a.denom *= -1; 00201 if (b.denom < 0) 00202 b.denom *= -1; 00203 00204 /* BUG: Possible overflow here.. Also, doesn't properly deal with 00205 * reciprocal denominators. 00206 */ 00207 aa = a.num * a.denom; 00208 bb = b.num * b.denom; 00209 00210 if (aa == bb) 00211 return 0; 00212 if (aa > bb) 00213 return 1; 00214 return -1; 00215 }
QofNumeric qof_numeric_convert | ( | QofNumeric | in, | |
gint64 | denom, | |||
gint | how | |||
) |
Change the denominator of a QofNumeric value to the specified denominator under standard arguments 'denom' and 'how'.
Definition at line 662 of file qofnumeric.c.
00663 { 00664 QofNumeric out; 00665 QofNumeric temp; 00666 gint64 temp_bc; 00667 gint64 temp_a; 00668 gint64 remainder; 00669 gint64 sign; 00670 gint denom_neg = 0; 00671 gdouble ratio, logratio; 00672 gdouble sigfigs; 00673 QofInt128 nume, newm; 00674 00675 temp.num = 0; 00676 temp.denom = 0; 00677 00678 if (qof_numeric_check (in)) 00679 return qof_numeric_error (QOF_ERROR_ARG); 00680 00681 if (denom == QOF_DENOM_AUTO) 00682 { 00683 switch (how & QOF_NUMERIC_DENOM_MASK) 00684 { 00685 default: 00686 case QOF_HOW_DENOM_LCD: /* LCD is meaningless with AUTO in here */ 00687 case QOF_HOW_DENOM_EXACT: 00688 return in; 00689 break; 00690 00691 case QOF_HOW_DENOM_REDUCE: 00692 /* reduce the input to a relatively-prime fraction */ 00693 return qof_numeric_reduce (in); 00694 break; 00695 00696 case QOF_HOW_DENOM_FIXED: 00697 if (in.denom != denom) 00698 return qof_numeric_error (QOF_ERROR_DENOM_DIFF); 00699 else 00700 return in; 00701 break; 00702 00703 case QOF_HOW_DENOM_SIGFIG: 00704 ratio = fabs (qof_numeric_to_double (in)); 00705 if (ratio < 10e-20) 00706 logratio = 0; 00707 else 00708 { 00709 logratio = log10 (ratio); 00710 logratio = ((logratio > 0.0) ? 00711 (floor (logratio) + 1.0) : (ceil (logratio))); 00712 } 00713 sigfigs = QOF_HOW_GET_SIGFIGS (how); 00714 00715 if (sigfigs - logratio >= 0) 00716 denom = (gint64) (pow (10, sigfigs - logratio)); 00717 else 00718 denom = -((gint64) (pow (10, logratio - sigfigs))); 00719 00720 how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_NUMERIC_SIGFIGS_MASK; 00721 break; 00722 } 00723 } 00724 00725 /* Make sure we need to do the work */ 00726 if (in.denom == denom) 00727 return in; 00728 if (in.num == 0) 00729 { 00730 out.num = 0; 00731 out.denom = denom; 00732 return out; 00733 } 00734 00735 /* If the denominator of the input value is negative, get rid of that. */ 00736 if (in.denom < 0) 00737 { 00738 in.num = in.num * (-in.denom); /* BUG: overflow not handled. */ 00739 in.denom = 1; 00740 } 00741 00742 sign = (in.num < 0) ? -1 : 1; 00743 00744 /* If the denominator is less than zero, we are to interpret it as 00745 * the reciprocal of its magnitude. */ 00746 if (denom < 0) 00747 { 00748 00749 /* XXX FIXME: use 128-bit math here ... */ 00750 denom = -denom; 00751 denom_neg = 1; 00752 temp_a = (in.num < 0) ? -in.num : in.num; 00753 temp_bc = in.denom * denom; /* BUG: overflow not handled. */ 00754 remainder = temp_a % temp_bc; 00755 out.num = temp_a / temp_bc; 00756 out.denom = -denom; 00757 } 00758 else 00759 { 00760 /* Do all the modulo and int division on positive values to make 00761 * things a little clearer. Reduce the fraction denom/in.denom to 00762 * help with range errors */ 00763 temp.num = denom; 00764 temp.denom = in.denom; 00765 temp = qof_numeric_reduce (temp); 00766 00767 /* Symbolically, do the following: 00768 * out.num = in.num * temp.num; 00769 * remainder = out.num % temp.denom; 00770 * out.num = out.num / temp.denom; 00771 * out.denom = denom; 00772 */ 00773 nume = mult128 (in.num, temp.num); 00774 newm = div128 (nume, temp.denom); 00775 remainder = rem128 (nume, temp.denom); 00776 00777 if (newm.isbig) 00778 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00779 00780 out.num = newm.lo; 00781 out.denom = denom; 00782 } 00783 00784 if (remainder) 00785 { 00786 switch (how & QOF_NUMERIC_RND_MASK) 00787 { 00788 case QOF_HOW_RND_FLOOR: 00789 if (sign < 0) 00790 out.num = out.num + 1; 00791 break; 00792 00793 case QOF_HOW_RND_CEIL: 00794 if (sign > 0) 00795 out.num = out.num + 1; 00796 break; 00797 00798 case QOF_HOW_RND_TRUNC: 00799 break; 00800 00801 case QOF_HOW_RND_PROMOTE: 00802 out.num = out.num + 1; 00803 break; 00804 00805 case QOF_HOW_RND_ROUND_HALF_DOWN: 00806 if (denom_neg) 00807 { 00808 if ((2 * remainder) > in.denom * denom) 00809 out.num = out.num + 1; 00810 } 00811 else if ((2 * remainder) > temp.denom) 00812 out.num = out.num + 1; 00813 /* check that 2*remainder didn't over-flow */ 00814 else if (((2 * remainder) < remainder) && 00815 (remainder > (temp.denom / 2))) 00816 out.num = out.num + 1; 00817 break; 00818 00819 case QOF_HOW_RND_ROUND_HALF_UP: 00820 if (denom_neg) 00821 { 00822 if ((2 * remainder) >= in.denom * denom) 00823 out.num = out.num + 1; 00824 } 00825 else if ((2 * remainder) >= temp.denom) 00826 out.num = out.num + 1; 00827 /* check that 2*remainder didn't over-flow */ 00828 else if (((2 * remainder) < remainder) && 00829 (remainder >= (temp.denom / 2))) 00830 out.num = out.num + 1; 00831 break; 00832 00833 case QOF_HOW_RND_ROUND: 00834 if (denom_neg) 00835 { 00836 if ((2 * remainder) > in.denom * denom) 00837 out.num = out.num + 1; 00838 else if ((2 * remainder) == in.denom * denom) 00839 { 00840 if (out.num % 2) 00841 out.num = out.num + 1; 00842 } 00843 } 00844 else 00845 { 00846 if ((2 * remainder) > temp.denom) 00847 out.num = out.num + 1; 00848 /* check that 2*remainder didn't over-flow */ 00849 else if (((2 * remainder) < remainder) && 00850 (remainder > (temp.denom / 2))) 00851 { 00852 out.num = out.num + 1; 00853 } 00854 else if ((2 * remainder) == temp.denom) 00855 { 00856 if (out.num % 2) 00857 out.num = out.num + 1; 00858 } 00859 /* check that 2*remainder didn't over-flow */ 00860 else if (((2 * remainder) < remainder) && 00861 (remainder == (temp.denom / 2))) 00862 { 00863 if (out.num % 2) 00864 out.num = out.num + 1; 00865 } 00866 } 00867 break; 00868 00869 case QOF_HOW_RND_NEVER: 00870 return qof_numeric_error (QOF_ERROR_REMAINDER); 00871 break; 00872 } 00873 } 00874 00875 out.num = (sign > 0) ? out.num : (-out.num); 00876 00877 return out; 00878 }
QofNumeric qof_numeric_convert_with_error | ( | QofNumeric | in, | |
gint64 | denom, | |||
gint | how, | |||
QofNumeric * | error | |||
) |
Same as QofNumeric_convert, but return a remainder value for accumulating conversion error.
static QofNumeric qof_numeric_create | ( | gint64 | num, | |
gint64 | denom | |||
) | [inline, static] |
Make a QofNumeric from numerator and denominator
Definition at line 243 of file qofnumeric.h.
00244 { 00245 QofNumeric out; 00246 out.num = num; 00247 out.denom = denom; 00248 return out; 00249 }
gchar* qof_numeric_dbg_to_string | ( | QofNumeric | n | ) |
Convert to string. Uses a static, non-thread-safe buffer. For internal use only.
Definition at line 1103 of file qofnumeric.c.
01104 { 01105 static gchar buff[1000]; 01106 static gchar *p = buff; 01107 gint64 tmpnum = n.num; 01108 gint64 tmpdenom = n.denom; 01109 01110 p += 100; 01111 if (p - buff >= 1000) 01112 p = buff; 01113 01114 sprintf (p, "%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, 01115 tmpdenom); 01116 01117 return p; 01118 }
static gint64 qof_numeric_denom | ( | QofNumeric | a | ) | [inline, static] |
QofNumeric qof_numeric_div | ( | QofNumeric | x, | |
QofNumeric | y, | |||
gint64 | denom, | |||
gint | how | |||
) |
Division. Note that division can overflow, in the following sense: if we write x=a/b and y=c/d then x/y = (a*d)/(b*c) If, after eliminating all common factors between the numerator (a*d) and the denominator (b*c), then if either the numerator and/or the denominator are *still* greater than 2^63, then the division has overflowed.
Definition at line 516 of file qofnumeric.c.
00517 { 00518 QofNumeric quotient; 00519 QofInt128 nume, deno; 00520 00521 if (qof_numeric_check (a) || qof_numeric_check (b)) 00522 return qof_numeric_error (QOF_ERROR_ARG); 00523 00524 if ((denom == QOF_DENOM_AUTO) && 00525 (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED) 00526 { 00527 if (a.denom == b.denom) 00528 denom = a.denom; 00529 else if (a.denom == 0) 00530 denom = b.denom; 00531 else 00532 return qof_numeric_error (QOF_ERROR_DENOM_DIFF); 00533 } 00534 00535 if (a.denom < 0) 00536 { 00537 a.num *= -a.denom; /* BUG: overflow not handled. */ 00538 a.denom = 1; 00539 } 00540 00541 if (b.denom < 0) 00542 { 00543 b.num *= -b.denom; /* BUG: overflow not handled. */ 00544 b.denom = 1; 00545 } 00546 00547 if (a.denom == b.denom) 00548 { 00549 quotient.num = a.num; 00550 quotient.denom = b.num; 00551 } 00552 else 00553 { 00554 gint64 sgn = 1; 00555 if (0 > a.num) 00556 { 00557 sgn = -sgn; 00558 a.num = -a.num; 00559 } 00560 if (0 > b.num) 00561 { 00562 sgn = -sgn; 00563 b.num = -b.num; 00564 } 00565 nume = mult128 (a.num, b.denom); 00566 deno = mult128 (b.num, a.denom); 00567 00568 /* Try to avoid overflow by removing common factors */ 00569 if (nume.isbig && deno.isbig) 00570 { 00571 QofNumeric ra = qof_numeric_reduce (a); 00572 QofNumeric rb = qof_numeric_reduce (b); 00573 gint64 gcf_nume = gcf64 (ra.num, rb.num); 00574 gint64 gcf_deno = gcf64 (rb.denom, ra.denom); 00575 00576 nume = mult128 (ra.num / gcf_nume, rb.denom / gcf_deno); 00577 deno = mult128 (rb.num / gcf_nume, ra.denom / gcf_deno); 00578 } 00579 00580 if ((0 == nume.isbig) && (0 == deno.isbig)) 00581 { 00582 quotient.num = sgn * nume.lo; 00583 quotient.denom = deno.lo; 00584 goto dive_done; 00585 } 00586 else if (0 == deno.isbig) 00587 { 00588 quotient = reduce128 (nume, deno.lo); 00589 if (0 == qof_numeric_check (quotient)) 00590 { 00591 quotient.num *= sgn; 00592 goto dive_done; 00593 } 00594 } 00595 00596 /* If rounding allowed, then shift until there's no 00597 * more overflow. The conversion at the end will fix 00598 * things up for the final value. */ 00599 if ((how & QOF_NUMERIC_RND_MASK) == QOF_HOW_RND_NEVER) 00600 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00601 while (nume.isbig || deno.isbig) 00602 { 00603 nume = shift128 (nume); 00604 deno = shift128 (deno); 00605 } 00606 quotient.num = sgn * nume.lo; 00607 quotient.denom = deno.lo; 00608 if (0 == quotient.denom) 00609 { 00610 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00611 } 00612 } 00613 00614 if (quotient.denom < 0) 00615 { 00616 quotient.num = -quotient.num; 00617 quotient.denom = -quotient.denom; 00618 } 00619 00620 dive_done: 00621 if ((denom == QOF_DENOM_AUTO) && 00622 ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD)) 00623 { 00624 denom = qof_numeric_lcd (a, b); 00625 how = how & QOF_NUMERIC_RND_MASK; 00626 } 00627 00628 return qof_numeric_convert (quotient, denom, how); 00629 }
QofNumeric qof_numeric_div_with_error | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how, | |||
QofNumeric * | error | |||
) |
The same as QofNumeric_div, but uses error for accumulating conversion roundoff error.
Definition at line 1071 of file qofnumeric.c.
01073 { 01074 QofNumeric quot = qof_numeric_div (a, b, denom, how); 01075 QofNumeric exact = qof_numeric_div (a, b, QOF_DENOM_AUTO, 01076 QOF_HOW_DENOM_REDUCE); 01077 QofNumeric err = qof_numeric_sub (quot, exact, 01078 QOF_DENOM_AUTO, QOF_HOW_DENOM_REDUCE); 01079 if (error) 01080 *error = err; 01081 return quot; 01082 }
gboolean qof_numeric_eq | ( | QofNumeric | a, | |
QofNumeric | b | |||
) |
Equivalence predicate: Returns TRUE (1) if a and b are exactly the same (have the same numerator and denominator)
Definition at line 223 of file qofnumeric.c.
gboolean qof_numeric_equal | ( | QofNumeric | a, | |
QofNumeric | b | |||
) |
Equivalence predicate: Returns TRUE (1) if a and b represent the same number. That is, return TRUE if the ratios, when reduced by eliminating common factors, are identical.
Definition at line 233 of file qofnumeric.c.
00234 { 00235 QofInt128 l, r; 00236 00237 if ((a.denom == b.denom) && (a.denom > 0)) 00238 return (a.num == b.num); 00239 if ((a.denom > 0) && (b.denom > 0)) 00240 { 00241 // return (a.num*b.denom == b.num*a.denom); 00242 l = mult128 (a.num, b.denom); 00243 r = mult128 (b.num, a.denom); 00244 return equal128 (l, r); 00245 00246 #if ALT_WAY_OF_CHECKING_EQUALITY 00247 QofNumeric ra = QofNumeric_reduce (a); 00248 QofNumeric rb = QofNumeric_reduce (b); 00249 if (ra.denom != rb.denom) 00250 return 0; 00251 if (ra.num != rb.num) 00252 return 0; 00253 return 1; 00254 #endif 00255 } 00256 if ((a.denom < 0) && (b.denom < 0)) 00257 { 00258 l = mult128 (a.num, -a.denom); 00259 r = mult128 (b.num, -b.denom); 00260 return equal128 (l, r); 00261 } 00262 else 00263 { 00264 /* BUG: One of the numbers has a reciprocal denom, and the other 00265 does not. I just don't know to handle this case in any 00266 reasonably overflow-proof yet simple way. So, this funtion 00267 will simply get it wrong whenever the three multiplies 00268 overflow 64-bits. -CAS */ 00269 if (a.denom < 0) 00270 return ((a.num * -a.denom * b.denom) == b.num); 00271 else 00272 return (a.num == (b.num * a.denom * -b.denom)); 00273 } 00274 return ((a.num * b.denom) == (a.denom * b.num)); 00275 }
QofNumeric qof_numeric_error | ( | QofNumericErrorCode | error_code | ) |
Create a QofNumeric object that signals the error condition noted by error_code, rather than a number.
Definition at line 1004 of file qofnumeric.c.
01005 { 01006 return qof_numeric_create (error_code, 0LL); 01007 }
QofNumeric qof_numeric_from_double | ( | gdouble | in, | |
gint64 | denom, | |||
gint | how | |||
) |
Convert a floating-point number to a QofNumeric. Both 'denom' and 'how' are used as in arithmetic, but QOF_DENOM_AUTO is not recognized; a denominator must be specified either explicitctly or through sigfigs.
Definition at line 919 of file qofnumeric.c.
00920 { 00921 QofNumeric out; 00922 gint64 int_part = 0; 00923 gdouble frac_part; 00924 gint64 frac_int = 0; 00925 gdouble logval; 00926 gdouble sigfigs; 00927 00928 if ((denom == QOF_DENOM_AUTO) && (how & QOF_HOW_DENOM_SIGFIG)) 00929 { 00930 if (fabs (in) < 10e-20) 00931 logval = 0; 00932 else 00933 { 00934 logval = log10 (fabs (in)); 00935 logval = ((logval > 0.0) ? 00936 (floor (logval) + 1.0) : (ceil (logval))); 00937 } 00938 sigfigs = QOF_HOW_GET_SIGFIGS (how); 00939 if (sigfigs - logval >= 0) 00940 denom = (gint64) (pow (10, sigfigs - logval)); 00941 else 00942 denom = -((gint64) (pow (10, logval - sigfigs))); 00943 00944 how = how & ~QOF_HOW_DENOM_SIGFIG & ~QOF_NUMERIC_SIGFIGS_MASK; 00945 } 00946 00947 int_part = (gint64) (floor (fabs (in))); 00948 frac_part = in - (double) int_part; 00949 00950 int_part = int_part * denom; 00951 frac_part = frac_part * (double) denom; 00952 00953 switch (how & QOF_NUMERIC_RND_MASK) 00954 { 00955 case QOF_HOW_RND_FLOOR: 00956 frac_int = (gint64) floor (frac_part); 00957 break; 00958 00959 case QOF_HOW_RND_CEIL: 00960 frac_int = (gint64) ceil (frac_part); 00961 break; 00962 00963 case QOF_HOW_RND_TRUNC: 00964 frac_int = (gint64) frac_part; 00965 break; 00966 00967 case QOF_HOW_RND_ROUND: 00968 case QOF_HOW_RND_ROUND_HALF_UP: 00969 frac_int = (gint64) rint (frac_part); 00970 break; 00971 00972 case QOF_HOW_RND_NEVER: 00973 frac_int = (gint64) floor (frac_part); 00974 if (frac_part != (double) frac_int) 00975 { 00976 /* signal an error */ 00977 } 00978 break; 00979 } 00980 00981 out.num = int_part + frac_int; 00982 out.denom = denom; 00983 return out; 00984 }
gboolean qof_numeric_from_string | ( | const gchar * | str, | |
QofNumeric * | n | |||
) |
Read a QofNumeric from str, skipping any leading whitespace. Return TRUE on success and store the resulting value in "n". Return NULL on error.
Definition at line 1121 of file qofnumeric.c.
01122 { 01123 size_t num_read; 01124 gint64 tmpnum; 01125 gint64 tmpdenom; 01126 01127 if (!str) 01128 return FALSE; 01129 01130 #ifdef QOF_DEPRECATED 01131 /* must use "<" here because %n's effects aren't well defined */ 01132 if (sscanf (str, " " QOF_SCANF_LLD "/" QOF_SCANF_LLD "%n", 01133 &tmpnum, &tmpdenom, &num_read) < 2) 01134 { 01135 return FALSE; 01136 } 01137 #else 01138 tmpnum = strtoll (str, NULL, 0); 01139 str = strchr (str, '/'); 01140 if (!str) 01141 return FALSE; 01142 str++; 01143 tmpdenom = strtoll (str, NULL, 0); 01144 num_read = strspn (str, "0123456789"); 01145 #endif 01146 n->num = tmpnum; 01147 n->denom = tmpdenom; 01148 return TRUE; 01149 }
QofNumeric qof_numeric_mul | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how | |||
) |
Multiply a times b, returning the product. An overflow may occur if the result of the multiplication can't be represented as a ratio of 64-bit int's after removing common factors.
Definition at line 405 of file qofnumeric.c.
00406 { 00407 QofNumeric product, result; 00408 QofInt128 bignume, bigdeno; 00409 00410 if (qof_numeric_check (a) || qof_numeric_check (b)) 00411 return qof_numeric_error (QOF_ERROR_ARG); 00412 00413 if ((denom == QOF_DENOM_AUTO) && 00414 (how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_FIXED) 00415 { 00416 if (a.denom == b.denom) 00417 denom = a.denom; 00418 else if (b.num == 0) 00419 denom = a.denom; 00420 else if (a.num == 0) 00421 denom = b.denom; 00422 else 00423 return qof_numeric_error (QOF_ERROR_DENOM_DIFF); 00424 } 00425 00426 if ((denom == QOF_DENOM_AUTO) && 00427 ((how & QOF_NUMERIC_DENOM_MASK) == QOF_HOW_DENOM_LCD)) 00428 { 00429 denom = qof_numeric_lcd (a, b); 00430 how = how & QOF_NUMERIC_RND_MASK; 00431 } 00432 00433 if (a.denom < 0) 00434 { 00435 a.num *= -a.denom; /* BUG: overflow not handled. */ 00436 a.denom = 1; 00437 } 00438 00439 if (b.denom < 0) 00440 { 00441 b.num *= -b.denom; /* BUG: overflow not handled. */ 00442 b.denom = 1; 00443 } 00444 00445 bignume = mult128 (a.num, b.num); 00446 bigdeno = mult128 (a.denom, b.denom); 00447 product.num = a.num * b.num; 00448 product.denom = a.denom * b.denom; 00449 00450 /* If it looks to be overflowing, try to reduce the fraction ... */ 00451 if (bignume.isbig || bigdeno.isbig) 00452 { 00453 gint64 tmp; 00454 00455 a = qof_numeric_reduce (a); 00456 b = qof_numeric_reduce (b); 00457 tmp = a.num; 00458 a.num = b.num; 00459 b.num = tmp; 00460 a = qof_numeric_reduce (a); 00461 b = qof_numeric_reduce (b); 00462 bignume = mult128 (a.num, b.num); 00463 bigdeno = mult128 (a.denom, b.denom); 00464 product.num = a.num * b.num; 00465 product.denom = a.denom * b.denom; 00466 } 00467 00468 /* If it its still overflowing, and rounding is allowed then round */ 00469 if (bignume.isbig || bigdeno.isbig) 00470 { 00471 /* If rounding allowed, then shift until there's no 00472 * more overflow. The conversion at the end will fix 00473 * things up for the final value. Else overflow. */ 00474 if ((how & QOF_NUMERIC_RND_MASK) == QOF_HOW_RND_NEVER) 00475 { 00476 if (bigdeno.isbig) 00477 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00478 product = reduce128 (bignume, product.denom); 00479 if (qof_numeric_check (product)) 00480 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00481 } 00482 else 00483 { 00484 while (bignume.isbig || bigdeno.isbig) 00485 { 00486 bignume = shift128 (bignume); 00487 bigdeno = shift128 (bigdeno); 00488 } 00489 product.num = bignume.lo; 00490 if (bignume.isneg) 00491 product.num = -product.num; 00492 00493 product.denom = bigdeno.lo; 00494 if (0 == product.denom) 00495 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00496 } 00497 } 00498 00499 #if 0 /* currently, product denom won't ever be zero */ 00500 if (product.denom < 0) 00501 { 00502 product.num = -product.num; 00503 product.denom = -product.denom; 00504 } 00505 #endif 00506 00507 result = qof_numeric_convert (product, denom, how); 00508 return result; 00509 }
QofNumeric qof_numeric_mul_with_error | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how, | |||
QofNumeric * | error | |||
) |
The same as QofNumeric_mul, but uses error for accumulating conversion roundoff error.
Definition at line 1052 of file qofnumeric.c.
01054 { 01055 QofNumeric prod = qof_numeric_mul (a, b, denom, how); 01056 QofNumeric exact = qof_numeric_mul (a, b, QOF_DENOM_AUTO, 01057 QOF_HOW_DENOM_REDUCE); 01058 QofNumeric err = qof_numeric_sub (prod, exact, QOF_DENOM_AUTO, 01059 QOF_HOW_DENOM_REDUCE); 01060 if (error) 01061 *error = err; 01062 return prod; 01063 }
QofNumeric qof_numeric_neg | ( | QofNumeric | a | ) |
Negate the argument
Definition at line 637 of file qofnumeric.c.
00638 { 00639 if (qof_numeric_check (a)) 00640 return qof_numeric_error (QOF_ERROR_ARG); 00641 return qof_numeric_create (-a.num, a.denom); 00642 }
gboolean qof_numeric_negative_p | ( | QofNumeric | a | ) |
Returns 1 if a < 0, otherwise returns 0.
Definition at line 137 of file qofnumeric.c.
00138 { 00139 if (qof_numeric_check (a)) 00140 return 0; 00141 else 00142 { 00143 if ((a.num < 0) && (a.denom != 0)) 00144 return 1; 00145 else 00146 return 0; 00147 } 00148 }
static gint64 qof_numeric_num | ( | QofNumeric | a | ) | [inline, static] |
gboolean qof_numeric_positive_p | ( | QofNumeric | a | ) |
Returns 1 if a > 0, otherwise returns 0.
Definition at line 155 of file qofnumeric.c.
00156 { 00157 if (qof_numeric_check (a)) 00158 return 0; 00159 else 00160 { 00161 if ((a.num > 0) && (a.denom != 0)) 00162 return 1; 00163 else 00164 return 0; 00165 } 00166 }
QofNumeric qof_numeric_reduce | ( | QofNumeric | in | ) |
Return input after reducing it by Greated Common Factor (GCF) elimination
Definition at line 888 of file qofnumeric.c.
00889 { 00890 gint64 t; 00891 gint64 num = (in.num < 0) ? (-in.num) : in.num; 00892 gint64 denom = in.denom; 00893 QofNumeric out; 00894 00895 if (qof_numeric_check (in)) 00896 return qof_numeric_error (QOF_ERROR_ARG); 00897 00898 /* The strategy is to use Euclid's algorithm */ 00899 while (denom > 0) 00900 { 00901 t = num % denom; 00902 num = denom; 00903 denom = t; 00904 } 00905 /* num now holds the GCD (Greatest Common Divisor) */ 00906 00907 /* All calculations are done on positive num, since it's not 00908 * well defined what % does for negative values */ 00909 out.num = in.num / num; 00910 out.denom = in.denom / num; 00911 return out; 00912 }
gint qof_numeric_same | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how | |||
) |
Equivalence predicate: Convert both a and b to denom using the specified DENOM and method HOW, and compare numerators the results using QofNumeric_equal.
For example, if a == 7/16 and b == 3/4, QofNumeric_same(a, b, 2, QOF_HOW_RND_TRUNC) == 1 because both 7/16 and 3/4 round to 1/2 under truncation. However, QofNumeric_same(a, b, 2, QOF_HOW_RND_ROUND) == 0 because 7/16 rounds to 1/2 under unbiased rounding but 3/4 rounds to 2/2.
Definition at line 285 of file qofnumeric.c.
00286 { 00287 QofNumeric aconv, bconv; 00288 00289 aconv = qof_numeric_convert (a, denom, how); 00290 bconv = qof_numeric_convert (b, denom, how); 00291 00292 return (qof_numeric_equal (aconv, bconv)); 00293 }
QofNumeric qof_numeric_sub | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how | |||
) |
Return a-b.
Definition at line 388 of file qofnumeric.c.
00389 { 00390 QofNumeric nb; 00391 00392 if (qof_numeric_check (a) || qof_numeric_check (b)) 00393 return qof_numeric_error (QOF_ERROR_ARG); 00394 00395 nb = b; 00396 nb.num = -nb.num; 00397 return qof_numeric_add (a, nb, denom, how); 00398 }
static QofNumeric qof_numeric_sub_fixed | ( | QofNumeric | a, | |
QofNumeric | b | |||
) | [inline, static] |
Shortcut for most common case: QofNumeric_sub(a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER);
Definition at line 422 of file qofnumeric.h.
00423 { 00424 return qof_numeric_sub (a, b, QOF_DENOM_AUTO, 00425 QOF_HOW_DENOM_FIXED | QOF_HOW_RND_NEVER); 00426 }
QofNumeric qof_numeric_sub_with_error | ( | QofNumeric | a, | |
QofNumeric | b, | |||
gint64 | denom, | |||
gint | how, | |||
QofNumeric * | error | |||
) |
The same as QofNumeric_sub, but uses error for accumulating conversion roundoff error.
Definition at line 1034 of file qofnumeric.c.
01036 { 01037 QofNumeric diff = qof_numeric_sub (a, b, denom, how); 01038 QofNumeric exact = qof_numeric_sub (a, b, QOF_DENOM_AUTO, 01039 QOF_HOW_DENOM_REDUCE); 01040 QofNumeric err = qof_numeric_sub (diff, exact, QOF_DENOM_AUTO, 01041 QOF_HOW_DENOM_REDUCE); 01042 if (error) 01043 *error = err; 01044 return diff; 01045 }
gdouble qof_numeric_to_double | ( | QofNumeric | in | ) |
Convert numeric to floating-point value.
Definition at line 991 of file qofnumeric.c.
00992 { 00993 if (in.denom > 0) 00994 return (gdouble) in.num / (gdouble) in.denom; 00995 else 00996 return (gdouble) (in.num * -in.denom); 00997 }
gchar* qof_numeric_to_string | ( | QofNumeric | n | ) |
Convert to string. The returned buffer is to be g_free'd by the caller (it was allocated through g_strdup)
Definition at line 1089 of file qofnumeric.c.
01090 { 01091 gchar *result; 01092 gint64 tmpnum = n.num; 01093 gint64 tmpdenom = n.denom; 01094 01095 result = 01096 g_strdup_printf ("%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, tmpnum, 01097 tmpdenom); 01098 01099 return result; 01100 }
static QofNumeric qof_numeric_zero | ( | void | ) | [inline, static] |
create a zero-value QofNumeric
Definition at line 253 of file qofnumeric.h.
00254 { 00255 return qof_numeric_create (0, 1); 00256 }
gboolean qof_numeric_zero_p | ( | QofNumeric | a | ) |
Returns 1 if the given QofNumeric is 0 (zero), else returns 0.
Definition at line 119 of file qofnumeric.c.
00120 { 00121 if (qof_numeric_check (a)) 00122 return 0; 00123 else 00124 { 00125 if ((a.num == 0) && (a.denom != 0)) 00126 return 1; 00127 else 00128 return 0; 00129 } 00130 }