[Gnucash-changes] bug-fix for how large-number division is done bug fix for large-number

Linas Vepstas linas at cvs.gnucash.org
Sun Jun 27 18:35:30 EDT 2004


Log Message:
-----------
bug-fix for how large-number division is done
bug fix for large-number conversion
-- (there are still some bugs, and some print statements, which I'll
remove shortly)

Modified Files:
--------------
    gnucash/src/engine:
        gnc-numeric.c

Revision Data
-------------
Index: gnc-numeric.c
===================================================================
RCS file: /home/cvs/cvsroot/gnucash/src/engine/gnc-numeric.c,v
retrieving revision 1.45
retrieving revision 1.46
diff -Lsrc/engine/gnc-numeric.c -Lsrc/engine/gnc-numeric.c -u -r1.45 -r1.46
--- src/engine/gnc-numeric.c
+++ src/engine/gnc-numeric.c
@@ -530,25 +530,44 @@
       gnc_numeric ra = gnc_numeric_reduce (a);
       gnc_numeric rb = gnc_numeric_reduce (b);
 
-      gint64 gcf_nume = gcf64(ra.num, rb.denom);
-      qofint128 nume = mult128(ra.num, rb.denom/gcf_nume);
+      gint64 gcf_nume = gcf64(ra.num, rb.num);
+      gint64 gcf_deno = gcf64(rb.denom, ra.denom);
+      qofint128 rnume = mult128(ra.num/gcf_nume, rb.denom/gcf_deno);
+      qofint128 rdeno = mult128(rb.num/gcf_nume, ra.denom/gcf_deno);
 
-      gint64 gcf_deno = gcf64(rb.num, ra.denom);
-      qofint128 deno = mult128(rb.num, ra.denom/gcf_deno);
-
-      if ((0 == nume.isbig) && (0 == deno.isbig))
+      if ((0 == rnume.isbig) && (0 == rdeno.isbig))
       {
-        quotient.num = sgn * nume.lo;
-        quotient.denom = deno.lo;
+        quotient.num = sgn * rnume.lo;
+        quotient.denom = rdeno.lo;
       }
-      else if (0 == deno.isbig)
+      else if (0 == rdeno.isbig)
       {
-        quotient = reduce128 (nume, deno.lo);
+        quotient = reduce128 (rnume, rdeno.lo);
         quotient.num *= sgn;
       }
       else
       {
-        return gnc_numeric_error (GNC_ERROR_OVERFLOW);
+        /* If not exact/fixed, and rounding allowed, then
+         * shift until there's no more overflow. The conversion
+         * at the end will fix things up the final value. */
+        if (((how & GNC_NUMERIC_RND_MASK) == GNC_HOW_RND_NEVER) ||
+            ((how & GNC_NUMERIC_DENOM_MASK) == GNC_HOW_DENOM_EXACT))
+        {
+          return gnc_numeric_error (GNC_ERROR_OVERFLOW);
+        }
+printf ("duude start shift nu=%llx %llx (%d) / %llx %llx %d\n", rnume.hi,
+rnume.lo, rnume.isbig, rdeno.hi, rdeno.lo, rdeno.isbig);
+        while (rnume.isbig || rdeno.isbig)
+        {
+           rnume = shift128 (rnume);
+           rdeno = shift128 (rdeno);
+printf ("duude shift nu=%llx %llx (%d) / %llx %llx %d\n", rnume.hi,
+rnume.lo, rnume.isbig, rdeno.hi, rdeno.lo, rdeno.isbig);
+        }
+double rat=((double)rnume.lo) / ((double) rdeno.lo);
+printf ("duude reduced raat=%g\n", rat);
+        quotient.num = sgn * rnume.lo;
+        quotient.denom = rdeno.lo;
       }
     }
   }
@@ -695,15 +714,27 @@
   {
     /* Do all the modulo and int division on positive values to make
      * things a little clearer. Reduce the fraction denom/in.denom to
-     * help with range errors (FIXME : need bigger intermediate rep) */
+     * help with range errors */
     temp.num   = denom;
     temp.denom = in.denom;
     temp       = gnc_numeric_reduce(temp);
-  
-    out.num   = in.num * temp.num;
-    out.num   = (out.num < 0) ? -out.num : out.num;
-    remainder = out.num % temp.denom;
-    out.num   = out.num / temp.denom;
+
+    /* Symbolically, do the following:
+     * out.num   = in.num * temp.num;
+     * remainder = out.num % temp.denom;
+     * out.num   = out.num / temp.denom;
+     * out.denom = denom;
+     */
+    qofint128 nume = mult128 (in.num, temp.num);
+    qofint128 newm = div128 (nume, temp.denom);
+    remainder = rem128 (nume, temp.denom);
+
+    if (newm.isbig)
+    {
+       return gnc_numeric_error(GNC_ERROR_OVERFLOW);
+    }
+
+    out.num = newm.lo;
     out.denom = denom;
   }
 
@@ -899,10 +930,14 @@
 double
 gnc_numeric_to_double(gnc_numeric in) 
 {
-  if(in.denom >= 0) {
+  if(in.denom >= 0) 
+  {
+printf ("duude to touble %g / %g = %g\n", (double)in.num, (double)in.denom ,
+(double)in.num/(double)in.denom);
     return (double)in.num/(double)in.denom;
   }
-  else {
+  else 
+  {
     return (double)(in.num * in.denom);
   }
 }


More information about the gnucash-changes mailing list