Fix for pr1887168.

This patch fixes some bugs in the implementation of signed integer
division with wide operands in constant expressions and adds support
for signed integer modulus with wide operands in constant expressions.
It also removes a few redundant lines of code.
This commit is contained in:
Martin Whitaker 2008-02-05 18:33:13 +00:00 committed by Stephen Williams
parent aaf8908676
commit 3d2e791e52
1 changed files with 40 additions and 26 deletions

View File

@ -940,7 +940,7 @@ verinum operator >> (const verinum&that, unsigned shift)
return result;
}
static verinum unsigned_divide(verinum num, verinum den)
static verinum unsigned_divide(verinum num, verinum den, bool signed_result)
{
unsigned nwid = num.len();
while (nwid > 0 && (num.get(nwid-1) == verinum::V0))
@ -956,7 +956,11 @@ static verinum unsigned_divide(verinum num, verinum den)
den = den << (nwid-dwid);
unsigned idx = nwid - dwid + 1;
verinum result (verinum::V0, idx);
verinum result (verinum::V0, signed_result ? idx + 1 : idx);
if (signed_result) {
result.set(idx, verinum::V0);
result.has_sign(true);
}
while (idx > 0) {
if (den <= num) {
verinum dif = num - den;
@ -986,12 +990,10 @@ static verinum unsigned_modulus(verinum num, verinum den)
den = den << (nwid-dwid);
unsigned idx = nwid - dwid + 1;
verinum result (verinum::V0, idx);
while (idx > 0) {
if (den <= num) {
verinum dif = num - den;
num = dif;
result.set(idx-1, verinum::V1);
}
den = den >> 1;
idx -= 1;
@ -1047,20 +1049,21 @@ verinum operator / (const verinum&left, const verinum&right)
} else {
verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false;
if (left < zero) {
use_left = zero - left;
negative ^= negative;
negative = !negative;
} else {
use_left = left;
}
if (right < zero) {
use_right = zero - right;
negative ^= negative;
negative = !negative;
} else {
use_right = right;
}
result = unsigned_divide(use_left, use_right);
result = unsigned_divide(use_left, use_right, true);
if (negative) result = zero - result;
}
@ -1078,7 +1081,7 @@ verinum operator / (const verinum&left, const verinum&right)
}
} else {
result = unsigned_divide(left, right);
result = unsigned_divide(left, right, false);
}
}
@ -1112,27 +1115,38 @@ verinum operator % (const verinum&left, const verinum&right)
result.has_sign(left.has_sign() || right.has_sign());
if (result.has_sign()) {
/* XXXX FIXME XXXX Use native unsigned division to do
the work. This does not work if the result is too
large for the native integer. */
assert(use_len <= 8*sizeof(long));
long l = left.as_long();
long r = right.as_long();
long v = l % r;
for (unsigned idx = 0 ; idx < use_len ; idx += 1) {
result.set(idx, (v & 1)? verinum::V1 : verinum::V0);
v >>= 1;
if (use_len <= 8*sizeof(long)) {
/* Use native signed modulus to do the work. */
long l = left.as_long();
long r = right.as_long();
long v = l % r;
for (unsigned idx = 0 ; idx < use_len ; idx += 1) {
result.set(idx, (v & 1)? verinum::V1 : verinum::V0);
v >>= 1;
}
} else {
verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false;
if (left < zero) {
use_left = zero - left;
negative = true;
} else {
use_left = left;
}
if (right < zero) {
use_right = zero - right;
} else {
use_right = right;
}
result = unsigned_modulus(use_left, use_right);
result.has_sign(true);
if (negative) result = zero - result;
}
} else {
/* Use native unsigned division, if possible, to do
the work. This does not work if the result is too
large for the native integer, so resort to a modulus
function in that case. */
if (use_len <= 8*sizeof(unsigned long)) {
assert(use_len <= 8*sizeof(unsigned long));
/* Use native unsigned modulus to do the work. */
unsigned long l = left.as_ulong();
unsigned long r = right.as_ulong();
unsigned long v = l % r;