Fix APInt long division algorithm

Summary: 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.

Test Plan: Unit test that reveals the bug included.

Reviewers: chandlerc, yaron.keren

Reviewed By: yaron.keren

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D9196

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@235699 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Pawel Bylica
2015-04-24 07:38:39 +00:00
parent a9a01d9879
commit 9ee9f1a334
2 changed files with 75 additions and 201 deletions

View File

@ -1586,28 +1586,18 @@ static void KnuthDiv(unsigned *u, unsigned *v, unsigned *q, unsigned* r,
// 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');