[PATCH] Fix APInt long division algorithm

Paweł Bylica chfast at gmail.com
Wed Apr 22 08:18:49 PDT 2015


Hi chandlerc,

This patch fixes step D4 of Knuth's division algorithm implementation. Negative sign of the step result was not always detected due to incorrect "borrow" handling.

REPOSITORY
  rL LLVM

http://reviews.llvm.org/D9196

Files:
  lib/Support/APInt.cpp
  unittests/ADT/APIntTest.cpp

Index: lib/Support/APInt.cpp
===================================================================
--- lib/Support/APInt.cpp
+++ lib/Support/APInt.cpp
@@ -1586,28 +1586,18 @@
     // this step is actually negative, (u[j+n]...u[j]) should be left as the
     // true value plus b**(n+1), namely as the b's complement of
     // the true value, and a "borrow" to the left should be remembered.
-    bool isNeg = false;
+    int64_t borrow = 0;
     for (unsigned i = 0; i < n; ++i) {
-      uint64_t u_tmp = (uint64_t(u[j+i+1]) << 32) | uint64_t(u[j+i]);
-      uint64_t subtrahend = uint64_t(qp) * uint64_t(v[i]);
-      bool borrow = subtrahend > u_tmp;
-      DEBUG(dbgs() << "KnuthDiv: u_tmp = " << u_tmp
-                   << ", subtrahend = " << subtrahend
+      uint64_t p = uint64_t(qp) * uint64_t(v[i]);
+      int64_t subres = int64_t(u[j+i]) - borrow - (unsigned)p;
+      u[j+i] = (unsigned)subres;
+      borrow = (p >> 32) - (subres >> 32);
+      DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j+i]
                    << ", borrow = " << borrow << '\n');
-
-      uint64_t result = u_tmp - subtrahend;
-      unsigned k = j + i;
-      u[k++] = (unsigned)result;         // subtraction low word
-      u[k++] = (unsigned)(result >> 32); // subtraction high word
-      while (borrow && k <= m+n) {       // deal with borrow to the left
-        borrow = u[k] == 0;
-        u[k]--;
-        k++;
-      }
-      isNeg |= borrow;
-      DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j+i] 
-                   << ", u[j+i+1] = " << u[j+i+1] << '\n');
     }
+    bool isNeg = u[j+n] < borrow;
+    u[j+n] -= (unsigned)borrow;
+
     DEBUG(dbgs() << "KnuthDiv: after subtraction:");
     DEBUG(for (int i = m+n; i >=0; i--) dbgs() << " " << u[i]);
     DEBUG(dbgs() << '\n');
Index: unittests/ADT/APIntTest.cpp
===================================================================
--- unittests/ADT/APIntTest.cpp
+++ unittests/ADT/APIntTest.cpp
@@ -409,6 +409,42 @@
   EXPECT_EQ(r, c);
 }
 
+TEST(APIntTest, divrem_big6) {
+  auto a = APInt{512, "10000000000000001000000000000001", 16};
+  auto b = APInt{512, "ffffffffffffffff00000000000000000000000001", 16};
+  auto c = APInt{512, "10000000000000000000000000000000", 16};
+
+  auto p = a * b + c;
+  auto q = p.udiv(a);
+  auto r = p.urem(a);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
+  APInt::udivrem(p, a, q, r);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
+  q = p.udiv(b);
+  r = p.urem(b);
+  EXPECT_EQ(a, q);
+  EXPECT_EQ(c, r);
+  APInt::udivrem(p, b, q, r);
+  EXPECT_EQ(a, q);
+  EXPECT_EQ(c, r);
+  q = p.sdiv(a);
+  r = p.srem(a);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
+  APInt::sdivrem(p, a, q, r);
+  EXPECT_EQ(b, q);
+  EXPECT_EQ(c, r);
+  q = p.sdiv(b);
+  r = p.srem(b);
+  EXPECT_EQ(a, q);
+  EXPECT_EQ(c, r);
+  APInt::sdivrem(p, b, q, r);
+  EXPECT_EQ(a, q);
+  EXPECT_EQ(c, r);
+}
+
 TEST(APIntTest, fromString) {
   EXPECT_EQ(APInt(32, 0), APInt(32,   "0", 2));
   EXPECT_EQ(APInt(32, 1), APInt(32,   "1", 2));

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D9196.24230.patch
Type: text/x-patch
Size: 2984 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150422/6d12ab70/attachment.bin>


More information about the llvm-commits mailing list