qofnumeric.c

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 =================== */

Generated on Thu Jan 31 22:50:26 2008 for QOF by  doxygen 1.5.4