QOF 0.7.5
|
00001 /******************************************************************** 00002 * qofnumeric.c -- an exact-number library for accounting use * 00003 * Copyright (C) 2000 Bill Gribble * 00004 * Copyright (C) 2004 Linas Vepstas <linas@linas.org> * 00005 * Copyright (c) 2006 Neil Williams <linux@codehelp.co.uk> * 00006 * * 00007 * This program is free software; you can redistribute it and/or * 00008 * modify it under the terms of the GNU General Public License as * 00009 * published by the Free Software Foundation; either version 2 of * 00010 * the License, or (at your option) any later version. * 00011 * * 00012 * This program is distributed in the hope that it will be useful, * 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00015 * GNU General Public License for more details. * 00016 * * 00017 * You should have received a copy of the GNU General Public License* 00018 * along with this program; if not, contact: * 00019 * * 00020 * Free Software Foundation Voice: +1-617-542-5942 * 00021 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00022 * Boston, MA 02110-1301, USA gnu@gnu.org * 00023 * * 00024 *******************************************************************/ 00025 00026 #include "config.h" 00027 00028 #include <glib.h> 00029 #include <math.h> 00030 #include <stdio.h> 00031 #include <stdlib.h> 00032 #include <string.h> 00033 00034 #include "qofnumeric.h" 00035 #include "qofmath128.c" 00036 00037 /*static QofLogModule log_module = QOF_MOD_ENGINE;*/ 00038 00039 /* =============================================================== */ 00040 /* This function is small, simple, and used everywhere below, 00041 * lets try to inline it. 00042 */ 00043 inline QofNumericErrorCode 00044 qof_numeric_check (QofNumeric in) 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 } 00057 00058 /* 00059 * Find the least common multiple of the denominators of a and b. 00060 */ 00061 00062 static inline gint64 00063 qof_numeric_lcd (QofNumeric a, QofNumeric b) 00064 { 00065 QofInt128 lcm; 00066 if (qof_numeric_check (a) || qof_numeric_check (b)) 00067 return QOF_ERROR_ARG; 00068 if (b.denom == a.denom) 00069 return a.denom; 00070 /* Special case: smaller divides smoothly into larger */ 00071 if ((b.denom < a.denom) && ((a.denom % b.denom) == 0)) 00072 return a.denom; 00073 if ((a.denom < b.denom) && ((b.denom % a.denom) == 0)) 00074 return b.denom; 00075 lcm = lcm128 (a.denom, b.denom); 00076 if (lcm.isbig) 00077 return QOF_ERROR_ARG; 00078 return lcm.lo; 00079 } 00080 00081 /* Return the ratio n/d reduced so that there are no common factors. */ 00082 static inline QofNumeric 00083 reduce128 (QofInt128 n, gint64 d) 00084 { 00085 gint64 t; 00086 gint64 num; 00087 gint64 denom; 00088 QofNumeric out; 00089 QofInt128 red; 00090 00091 t = rem128 (n, d); 00092 num = d; 00093 denom = t; 00094 00095 /* The strategy is to use Euclid's algorithm */ 00096 while (denom > 0) 00097 { 00098 t = num % denom; 00099 num = denom; 00100 denom = t; 00101 } 00102 /* num now holds the GCD (Greatest Common Divisor) */ 00103 00104 red = div128 (n, num); 00105 if (red.isbig) 00106 return qof_numeric_error (QOF_ERROR_OVERFLOW); 00107 out.num = red.lo; 00108 if (red.isneg) 00109 out.num = -out.num; 00110 out.denom = d / num; 00111 return out; 00112 } 00113 00114 /* ******************************************************************* 00115 * qof_numeric_zero_p 00116 ********************************************************************/ 00117 00118 gboolean 00119 qof_numeric_zero_p (QofNumeric a) 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 } 00131 00132 /* ******************************************************************* 00133 * qof_numeric_negative_p 00134 ********************************************************************/ 00135 00136 gboolean 00137 qof_numeric_negative_p (QofNumeric a) 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 } 00149 00150 /* ******************************************************************* 00151 * qof_numeric_positive_p 00152 ********************************************************************/ 00153 00154 gboolean 00155 qof_numeric_positive_p (QofNumeric a) 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 } 00167 00168 /* ******************************************************************* 00169 * qof_numeric_compare 00170 * returns 1 if a>b, -1 if b>a, 0 if a == b 00171 ********************************************************************/ 00172 00173 gint 00174 qof_numeric_compare (QofNumeric a, QofNumeric b) 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 } 00216 00217 00218 /* ******************************************************************* 00219 * qof_numeric_eq 00220 ********************************************************************/ 00221 00222 gboolean 00223 qof_numeric_eq (QofNumeric a, QofNumeric b) 00224 { 00225 return ((a.num == b.num) && (a.denom == b.denom)); 00226 } 00227 00228 /* ******************************************************************* 00229 * QofNumeric_equal 00230 ********************************************************************/ 00231 00232 gboolean 00233 qof_numeric_equal (QofNumeric a, QofNumeric b) 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 } 00276 00277 00278 /* ******************************************************************* 00279 * qof_numeric_same 00280 * would a and b be equal() if they were both converted to the same 00281 * denominator? 00282 ********************************************************************/ 00283 00284 gint 00285 qof_numeric_same (QofNumeric a, QofNumeric b, gint64 denom, gint how) 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 } 00294 00295 /* ******************************************************************* 00296 * qof_numeric_add 00297 ********************************************************************/ 00298 00299 QofNumeric 00300 qof_numeric_add (QofNumeric a, QofNumeric b, gint64 denom, gint how) 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 } 00382 00383 /* *************************************************************** 00384 * qof_numeric_sub 00385 *****************************************************************/ 00386 00387 QofNumeric 00388 qof_numeric_sub (QofNumeric a, QofNumeric b, gint64 denom, gint how) 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 } 00399 00400 /* *************************************************************** 00401 * qof_numeric_mul 00402 *****************************************************************/ 00403 00404 QofNumeric 00405 qof_numeric_mul (QofNumeric a, QofNumeric b, gint64 denom, gint how) 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 } 00510 00511 /* ******************************************************************* 00512 * qof_numeric_div 00513 ********************************************************************/ 00514 00515 QofNumeric 00516 qof_numeric_div (QofNumeric a, QofNumeric b, gint64 denom, gint how) 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 } 00630 00631 /* ******************************************************************* 00632 * qof_numeric_neg 00633 * negate the argument 00634 ********************************************************************/ 00635 00636 QofNumeric 00637 qof_numeric_neg (QofNumeric a) 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 } 00643 00644 /* ******************************************************************* 00645 * qof_numeric_neg 00646 * return the absolute value of the argument 00647 ********************************************************************/ 00648 00649 QofNumeric 00650 qof_numeric_abs (QofNumeric a) 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 } 00656 00657 /* ******************************************************************* 00658 * qof_numeric_convert 00659 ********************************************************************/ 00660 00661 QofNumeric 00662 qof_numeric_convert (QofNumeric in, gint64 denom, gint how) 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 } 00879 00880 /* ************************************************************* 00881 reduce a fraction by GCF elimination. This is NOT done as a 00882 part of the arithmetic API unless QOF_HOW_DENOM_REDUCE is 00883 specified 00884 as the output denominator. 00885 ****************************************************************/ 00886 00887 QofNumeric 00888 qof_numeric_reduce (QofNumeric in) 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 } 00913 00914 /* *************************************************************** 00915 * double_to_QofNumeric 00916 ****************************************************************/ 00917 00918 QofNumeric 00919 qof_numeric_from_double (gdouble in, gint64 denom, gint how) 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 } 00985 00986 /* ******************************************************************* 00987 * qof_numeric_to_double 00988 ********************************************************************/ 00989 00990 gdouble 00991 qof_numeric_to_double (QofNumeric in) 00992 { 00993 if (in.denom > 0) 00994 return (gdouble) in.num / (gdouble) in.denom; 00995 else 00996 return (gdouble) (in.num * -in.denom); 00997 } 00998 00999 /* ******************************************************************* 01000 * qof_numeric_error 01001 ********************************************************************/ 01002 01003 QofNumeric 01004 qof_numeric_error (QofNumericErrorCode error_code) 01005 { 01006 return qof_numeric_create (error_code, 0LL); 01007 } 01008 01009 /* ******************************************************************* 01010 * qof_numeric_add_with_error 01011 ********************************************************************/ 01012 01013 QofNumeric 01014 qof_numeric_add_with_error (QofNumeric a, QofNumeric b, 01015 gint64 denom, gint how, QofNumeric * error) 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 } 01028 01029 /* ******************************************************************* 01030 * qof_numeric_sub_with_error 01031 ********************************************************************/ 01032 01033 QofNumeric 01034 qof_numeric_sub_with_error (QofNumeric a, QofNumeric b, 01035 gint64 denom, gint how, QofNumeric * error) 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 } 01046 01047 /* ******************************************************************* 01048 * qof_numeric_mul_with_error 01049 ********************************************************************/ 01050 01051 QofNumeric 01052 qof_numeric_mul_with_error (QofNumeric a, QofNumeric b, 01053 gint64 denom, gint how, QofNumeric * error) 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 } 01064 01065 01066 /* ******************************************************************* 01067 * qof_numeric_div_with_error 01068 ********************************************************************/ 01069 01070 QofNumeric 01071 qof_numeric_div_with_error (QofNumeric a, QofNumeric b, 01072 gint64 denom, gint how, QofNumeric * error) 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 } 01083 01084 /* *************************************************************** 01085 * QofNumeric text IO 01086 ****************************************************************/ 01087 01088 gchar * 01089 qof_numeric_to_string (QofNumeric n) 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 } 01101 01102 gchar * 01103 qof_numeric_dbg_to_string (QofNumeric n) 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 } 01119 01120 gboolean 01121 qof_numeric_from_string (const gchar * str, QofNumeric * n) 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 } 01150 01151 /* *************************************************************** 01152 * qof_numeric misc testing 01153 ****************************************************************/ 01154 #ifdef _QOF_NUMERIC_TEST 01155 01156 static gchar * 01157 qof_numeric_print (QofNumeric in) 01158 { 01159 gchar *retval; 01160 if (qof_numeric_check (in)) 01161 { 01162 retval = 01163 g_strdup_printf ("<ERROR> [%" G_GINT64_FORMAT " / %" 01164 G_GINT64_FORMAT "]", in.num, in.denom); 01165 } 01166 else 01167 { 01168 retval = 01169 g_strdup_printf ("[%" G_GINT64_FORMAT " / %" G_GINT64_FORMAT 01170 "]", in.num, in.denom); 01171 } 01172 return retval; 01173 } 01174 01175 int 01176 main (void) 01177 { 01178 QofNumeric a = qof_numeric_create (1, 3); 01179 QofNumeric b = qof_numeric_create (1, 4); 01180 QofNumeric c; 01181 01182 QofNumeric err; 01183 01184 c = qof_numeric_add_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err); 01185 printf ("add 100ths/error : %s + %s = %s + (error) %s\n\n", 01186 qof_numeric_print (a), qof_numeric_print (b), 01187 qof_numeric_print (c), qof_numeric_print (err)); 01188 01189 c = qof_numeric_sub_with_error (a, b, 100, QOF_HOW_RND_FLOOR, &err); 01190 printf ("sub 100ths/error : %s - %s = %s + (error) %s\n\n", 01191 qof_numeric_print (a), qof_numeric_print (b), 01192 qof_numeric_print (c), qof_numeric_print (err)); 01193 01194 c = qof_numeric_mul_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err); 01195 printf ("mul 100ths/error : %s * %s = %s + (error) %s\n\n", 01196 qof_numeric_print (a), qof_numeric_print (b), 01197 qof_numeric_print (c), qof_numeric_print (err)); 01198 01199 c = qof_numeric_div_with_error (a, b, 100, QOF_HOW_RND_ROUND, &err); 01200 printf ("div 100ths/error : %s / %s = %s + (error) %s\n\n", 01201 qof_numeric_print (a), qof_numeric_print (b), 01202 qof_numeric_print (c), qof_numeric_print (err)); 01203 01204 printf ("multiply (EXACT): %s * %s = %s\n", 01205 qof_numeric_print (a), qof_numeric_print (b), 01206 qof_numeric_print (qof_numeric_mul 01207 (a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_EXACT))); 01208 01209 printf ("multiply (REDUCE): %s * %s = %s\n", 01210 qof_numeric_print (a), qof_numeric_print (b), 01211 qof_numeric_print (qof_numeric_mul 01212 (a, b, QOF_DENOM_AUTO, QOF_HOW_DENOM_REDUCE))); 01213 01214 01215 return 0; 01216 } 01217 #endif 01218 01219 /* ======================== END OF FILE =================== */