List:Internals« Previous MessageNext Message »
From:Sergei Golubchik Date:March 31 2005 5:46pm
Subject:bk commit into 5.0 tree (serg:1.1843) BUG#9501
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of serg. When serg does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1843 05/03/31 17:46:36 serg@stripped +3 -0
  decimal_div bug#9501

  strings/decimal.c
    1.43 05/03/31 17:46:27 serg@stripped +55 -68
    decimal_div bug#9501

  mysql-test/t/type_newdecimal.test
    1.4 05/03/31 17:46:27 serg@stripped +6 -1
    decimal_div bug#9501

  mysql-test/r/type_newdecimal.result
    1.5 05/03/31 17:46:27 serg@stripped +3 -0
    decimal_div bug#9501

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	serg
# Host:	serg.mylan
# Root:	/usr/home/serg/Abk/mysql-5.0

--- 1.4/mysql-test/r/type_newdecimal.result	Fri Mar 25 14:16:57 2005
+++ 1.5/mysql-test/r/type_newdecimal.result	Thu Mar 31 17:46:27 2005
@@ -839,3 +839,6 @@
 INSERT INTO Sow6_2f VALUES ('a59b');
 ERROR HY000: Incorrect decimal value: 'a59b' for column 'col1' at row 1
 drop table Sow6_2f;
+select 10.3330000000000/12.34500000;
+10.3330000000000/12.34500000
+0.8370190360469825840421223160000

--- 1.3/mysql-test/t/type_newdecimal.test	Fri Mar 25 14:16:36 2005
+++ 1.4/mysql-test/t/type_newdecimal.test	Thu Mar 31 17:46:27 2005
@@ -748,7 +748,7 @@
 #+-------+
 #| 1 / 0 |
 #+-------+
-#| NULL |
+#| NULL  |
 #+-------+
 #1 row in set, 1 warning (0.00 sec)
 #
@@ -864,3 +864,8 @@
 INSERT INTO Sow6_2f VALUES ('a59b');
 #-- should return SQLSTATE 22018 invalid character value for cast
 drop table Sow6_2f;
+
+#
+# bug#9501
+#
+select 10.3330000000000/12.34500000;

--- 1.42/strings/decimal.c	Mon Mar 21 13:58:29 2005
+++ 1.43/strings/decimal.c	Thu Mar 31 17:46:27 2005
@@ -1933,16 +1933,16 @@
   XXX if this library is to be used with huge numbers of thousands of
   digits, fast division must be implemented and alloca should be
   changed to malloc (or at least fallback to malloc if alloca() fails)
-  but then, decimal_mod() should be rewritten too :(
+  but then, decimal_mul() should be rewritten too :(
 */
 static int do_div_mod(decimal_t *from1, decimal_t *from2,
                        decimal_t *to, decimal_t *mod, int scale_incr)
 {
   int frac1=ROUND_UP(from1->frac)*DIG_PER_DEC1, prec1=from1->intg+frac1,
       frac2=ROUND_UP(from2->frac)*DIG_PER_DEC1, prec2=from2->intg+frac2,
-      error, i, intg0, frac0, len1, len2, dlen1, dintg, div=(!mod);
+      error, i, intg0, frac0, len1, len2, dintg, div=(!mod);
   dec1 *buf0, *buf1=from1->buf, *buf2=from2->buf, *tmp1,
-       *start2, *stop2, *stop1, *stop0, norm2, carry, *start1;
+       *start2, *stop2, *stop1, *stop0, norm2, carry, *start1, dcarry;
   dec2 norm_factor, x, guess, y;
 
   LINT_INIT(error);
@@ -2043,7 +2043,7 @@
   /* removing end zeroes */
   while (*stop2 == 0 && stop2 >= start2)
     stop2--;
-  len2= ++stop2 - start2;
+  len2= stop2++ - start2;
 
   /*
     calculating norm2 (normalized *start2) - we need *start2 to be large
@@ -2055,87 +2055,70 @@
   */
   norm_factor=DIG_BASE/(*start2+1);
   norm2=(dec1)(norm_factor*start2[0]);
-  if (likely(len2>1))
+  if (likely(len2>0))
     norm2+=(dec1)(norm_factor*start2[1]/DIG_BASE);
 
+  if (*start1 < *start2)
+    dcarry=*start1++;
+  else
+    dcarry=0;
+
   /* main loop */
-  for ( ; buf0 < stop0; buf0++)
+  for (; buf0 < stop0; buf0++)
   {
     /* short-circuit, if possible */
-    if (unlikely(*start1 == 0))
-    {
-      start1++;
-      if (likely(div))
-        *buf0=0;
-      continue;
-    }
-
-    /* D3: make a guess */
-    if (*start1 >= *start2)
-    {
-      x=start1[0];
-      y=start1[1];
-      dlen1=len2-1;
-    }
+    if (unlikely(dcarry == 0 && *start1 < *start2))
+      guess=0;
     else
     {
-      x=((dec2)start1[0])*DIG_BASE+start1[1];
-      y=start1[2];
-      dlen1=len2;
-    }
-    guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2;
-    if (unlikely(guess >= DIG_BASE))
-      guess=DIG_BASE-1;
-    if (likely(len2>1))
-    {
-      /* hmm, this is a suspicious trick - I removed normalization here */
-      if (start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y)
-        guess--;
-      if (unlikely(start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y))
-        guess--;
-      DBUG_ASSERT(start2[1]*guess <= (x-guess*start2[0])*DIG_BASE+y);
-    }
-
-    /* D4: multiply and subtract */
-    buf2=stop2;
-    buf1=start1+dlen1;
-    DBUG_ASSERT(buf1 < stop1);
-    for (carry=0; buf2 > start2; buf1--)
-    {
-      dec1 hi, lo;
-      x=guess * (*--buf2);
-      hi=(dec1)(x/DIG_BASE);
-      lo=(dec1)(x-((dec2)hi)*DIG_BASE);
-      SUB2(*buf1, *buf1, lo, carry);
-      carry+=hi;
-    }
-    for (; buf1 >= start1; buf1--)
-    {
-      SUB2(*buf1, *buf1, 0, carry);
-    }
+      /* D3: make a guess */
+      x=start1[0]+((dec2)dcarry)*DIG_BASE;
+      y=start1[1];
+      guess=(norm_factor*x+norm_factor*y/DIG_BASE)/norm2;
+      if (unlikely(guess >= DIG_BASE))
+        guess=DIG_BASE-1;
+      if (likely(len2>0))
+      {
+        /* hmm, this is a suspicious trick - I removed normalization here */
+        if (start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y)
+          guess--;
+        if (unlikely(start2[1]*guess > (x-guess*start2[0])*DIG_BASE+y))
+          guess--;
+        DBUG_ASSERT(start2[1]*guess <= (x-guess*start2[0])*DIG_BASE+y);
+      }
 
-    /* D5: check the remainder */
-    if (unlikely(carry))
-    {
-      DBUG_ASSERT(carry==1);
-      /* D6: correct the guess */
-      guess--;
+      /* D4: multiply and subtract */
       buf2=stop2;
-      buf1=start1+dlen1;
+      buf1=start1+len2;
+      DBUG_ASSERT(buf1 < stop1);
       for (carry=0; buf2 > start2; buf1--)
       {
-        ADD(*buf1, *buf1, *--buf2, carry);
+        dec1 hi, lo;
+        x=guess * (*--buf2);
+        hi=(dec1)(x/DIG_BASE);
+        lo=(dec1)(x-((dec2)hi)*DIG_BASE);
+        SUB2(*buf1, *buf1, lo, carry);
+        carry+=hi;
       }
-      for (; buf1 >= start1; buf1--)
+      carry= dcarry < carry;
+
+      /* D5: check the remainder */
+      if (unlikely(carry))
       {
-        SUB2(*buf1, *buf1, 0, carry);
+        /* D6: correct the guess */
+        guess--;
+        buf2=stop2;
+        buf1=start1+len2;
+        for (carry=0; buf2 > start2; buf1--)
+        {
+          ADD(*buf1, *buf1, *--buf2, carry);
+        }
       }
-      DBUG_ASSERT(carry==1);
     }
     if (likely(div))
       *buf0=(dec1)guess;
-    if (*start1 == 0)
-      start1++;
+    dcarry= *start1;
+    start1++;
   }
   if (mod)
   {
@@ -2144,6 +2127,8 @@
         intg=prec1-frac1
         frac=max(frac1, frac2)=to->frac
     */
+    if (dcarry)
+      *--start1=dcarry;
     buf0=to->buf;
     intg0=ROUND_UP(prec1-frac1)-(start1-tmp1);
     frac0=ROUND_UP(to->frac);
@@ -2753,6 +2738,8 @@
   test_dv("1.000000000000", "3","0.333333333333333333", 0);
   test_dv("1", "1","1.000000000", 0);
   test_dv("0.0123456789012345678912345",
"9999999999","0.000000000001234567890246913578148141", 0);
+  test_dv("10.333000000", "12.34500","0.837019036046982584042122316", 0);
+  test_dv("10.000000000060", "2","5.000000000030000000", 0);
 
   printf("==== decimal_mod ====\n");
   test_md("234","10","4", 0);
Thread
bk commit into 5.0 tree (serg:1.1843) BUG#9501Sergei Golubchik31 Mar