bignum.c
来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 1,299 行 · 第 1/4 页
C
1,299 行
// 4.3.1, Algorithm D for details. This function estimates
// how many times v goes into u, starting at u's jth digit.
// A digit here is actually a data element, thought of as
// being in the radix 2**sizeof(Data).)
// Inputs: reference to CoolBignum dividend and divisor and starting digit j
// Outputs: estimated number of times v goes into u
Data estimate_q_hat (const CoolBignum& u, const CoolBignum& v, Counter j) {
Data q_hat,
v1 = v.data[v.count - 1], // localize frequent data
v2 = v.data[v.count - 2],
u0 = u.data[u.count - 1 - j],
u1 = u.data[u.count - 2 - j],
u2 = u.data[u.count - 3 - j];
// Initial Knuth estimate, usually correct
q_hat = Data(u0 == v1 ?
radix_s - 1 :
(u0*radix_s + u1)/v1);
// high speed test to determine most of the cases in which initial
// estimate is too large. Eliminates most cases in which q_hat is one too
// large. Eliminates all cases in which q_hat is two too large. The test
// looks hairy because we have to watch out for overflow. In the book, this
// test is the simple inequality:
// v2*q_hat > (u0*radix_s + u1 - q_hat*v1)*radix_s + u2.
// If the inequality is true, decrease q_hat by 1. If inequality is still
// true, decrease q_hat again.
unsigned long lhs, rhs; // lhs, rhs of Knuth inequality
for (Counter i = 0; i < 2; i++) { // loop at most twice
lhs = v2 * q_hat; // Calculate left-hand side of ineq.
rhs = u0 * radix_s + u1; // Calculate part of right-hand side
rhs -= (q_hat * v1); // Now subtract off part
// DML: My attempt to fix the overflow testing bug..
double temp_rhs = double(rhs);
double temp_radix_s = double(radix_s);
// OLD WAY: if (rhs > rhs * radix_s)// if multiplication causes overflow
// NEW WAY: see if result won't fit into a long.
if ( temp_rhs * temp_radix_s > double(MAXLONG) )
break; // then rhs > lhs, so test fails
rhs *= radix_s; // No overflow: ok to multiply
temp_rhs = double(rhs);
double temp_u2 = double(u2);
// OLD WAY: if (rhs > rhs + u2) // if addition yields overflow
if ( temp_rhs + temp_u2 > double(MAXLONG) ) // NEW WAY.
break; // then rhs > lhs, so test fails
rhs += u2; // No overflow: ok to add.
if (lhs <= rhs) // if lhs <= rhs
break; // test fails
q_hat--; // Test passes: decrement q_hat
} // Loop again
return q_hat; // Return estimate
}
// Original version.
// Data estimate_q_hat (const CoolBignum& u, const CoolBignum& v, Counter j) {
// Data q_hat,
// v1 = v.data[v.count - 1], // localize frequent data
// v2 = v.data[v.count - 2],
// u0 = u.data[u.count - 1 - j],
// u1 = u.data[u.count - 2 - j],
// u2 = u.data[u.count - 3 - j];
//
// // Initial Knuth estimate, usually correct
// q_hat = Data(u0 == v1 ?
// radix_s - 1 :
// (u0*radix_s + u1)/v1);
//
// // high speed test to determine most of the cases in which initial
// // estimate is too large. Eliminates most cases in which q_hat is one too
// // large. Eliminates all cases in which q_hat is two too large. The test
// // looks hairy because we have to watch out for overflow. In the book, this
// // test is the simple inequality:
// // v2*q_hat > (u0*radix_s + u1 - q_hat*v1)*radix_s + u2.
// // If the inequality is true, decrease q_hat by 1. If inequality is still
// // true, decrease q_hat again.
// unsigned long lhs, rhs; // lhs, rhs of Knuth inequality
// for (Counter i = 0; i < 2; i++) { // loop at most twice
// lhs = v2 * q_hat; // Calculate left-hand side of ineq.
// rhs = u0 * radix_s + u1; // Calculate part of right-hand side
// rhs -= (q_hat * v1); // Now subtract off part
// if (rhs > rhs * radix_s) // if multiplication causes overflow
// break; // then rhs > lhs, so test fails
// rhs *= radix_s; // No overflow: ok to multiply
// if (rhs > rhs + u2) // if addition yields overflow
// break; // then rhs > lhs, so test fails
// rhs += u2; // No overflow: ok to add.
// if (lhs <= rhs) // if lhs <= rhs
// break; // test fails
// q_hat--; // Test passes: decrement q_hat
// } // Loop again
// return q_hat; // Return estimate
// }
// multiply_subtract -- calculate u - v*q_hat (Refer to Knuth, V. 2, Section
// 4.3.1, Algorithm D for details. A digit here is a
// data element, thought of as being in the radix
// 2**sizeof(Data).)
// Inputs: reference to CoolBignum dividend, divisor, estimated result, and index
// into jth digit of dividend
// Outputs: true number of times v goes into u
Data multiply_subtract (CoolBignum& u, const CoolBignum& v, Data q_hat, Counter j) {
// At this point it has been estimated that v goes into the jth and higher
// digits of u about q_hat times, and in fact that q_hat is either the
// correct number of times or one too large.
if (q_hat == 0) return q_hat; // if q_hat 0, nothing to do
CoolBignum rslt; // create a temporary CoolBignum
Counter tmpcnt;
rslt.data = // allocate data for it
new Data[rslt.count = v.count + 1];
// simultaneous computation of u - v*q_hat
unsigned long prod, diff;
Data carry = 0, borrow = 0;
Counter i;
for (i = 0; i < v.count; i++) {
// for each digit of v, multiply it by q_hat and subtract the result
prod = (unsigned long)v.data[i] * (unsigned long)q_hat + carry;
diff = (unsigned long)u.data[u.count - v.count - 1 - j + i]
+ (unsigned long)radix_s - borrow;
diff -= (unsigned long)Data(prod); // form proper digit of u
rslt.data[i] = Data(diff); // save the result
borrow = (diff/radix_s == 0); // keep track of any borrows
carry = Data(prod/radix_s); // keep track of carries
}
tmpcnt = u.count - v.count - 1 - j + i;
diff = (unsigned long)u.data[tmpcnt] // special case for the last
+ (unsigned long)radix_s - borrow; // digit
diff -= carry;
rslt.data[i] = Data(diff);
borrow = (diff/radix_s == 0);
// A leftover borrow indicates that u - v*q_hat is negative, i.e., that
// q_hat was one too large. So to get correct result, decrement q_hat and
// add back one multiple of v
if (borrow) {
q_hat--;
carry = 0;
unsigned long sum;
for (i = 0; i < v.count; i++) {
sum = (unsigned long)rslt.data[i] + (unsigned long)v.data[i] + carry;
carry = Data(sum/radix_s);
u.data[u.count - v.count - 1 - j + i] = Data(sum);
}
u.data[u.count - v.count - 1 - j + i] = rslt.data[i] + carry;
}
else { // otherwise, result is ok
for (i = 0; i < rslt.count; i++) // store result back into u
u.data[u.count - v.count - 1 - j + i] =
rslt.data[i];
}
return q_hat; // return corrected q_hat
}
// divide - divide b2 into b1, getting quotient q and remainder r. (Refer to
// Knuth, V.2, Seciton 4.3.1, Algorithm D for details. This function
// implements Algorithm D.)
// Inputs: references to a CoolBignum dividend b1, divisor b2, quotient q, and
// remainder r.
// Outputs: none
void divide (const CoolBignum& b1, const CoolBignum& b2, CoolBignum& q, CoolBignum& r) {
q = r = zero_s;
if (b1 == zero_s) // If divisor is zero
return; // return zero quotient and remainder
int mag = magnitude_cmp(b1,b2); // Compare magnitudes
if (mag < 0) // if abs(b1) < abs(b2)
r = b1; // return zero quotient, b1 remainder
else if (mag == 0) // if abs(b1) == abs(b2)
q = one_s; // quotient is 1, remainder is 0
else { // otherwise abs(b1) > abs(b2), so divide
q.data = new Data[q.count = b1.count - b2.count + 1]; // Allocate quotient
r.data = new Data[r.count = b2.count]; // Allocate remainder
if (b2.count == 1) { // Single digit divisor?
divide_aux(b1,b2.data[0],q,r.data[0]); // Do single digit divide
}
else { // Else full-blown divide
CoolBignum u,v;
Data d = normalize(b1,b2,u,v); // Set u = b1/d, v = b2/d
Data q_hat; // Multiplier
Counter j = 0;
while (j <= b1.count - b2.count) { // Main division loop
q_hat = estimate_q_hat(u,v,j); // Estimate # times v divides
q.data[q.count - 1 - j] = // Do division, get true answ.
multiply_subtract(u,v,q_hat,j);
j++;
}
static Data dufus; // dummy variable
divide_aux(u,d,r,dufus); // Unnormalize u for remainder
}
q.trim(); // Trim leading zeros of quot.
r.trim(); // Trim leading zeros of rem.
}
q.sign = r.sign = b1.sign * b2.sign; // Calculate signs
}
// operator/ -- overload division operator for CoolBignums
// Inputs: references to CoolBignum numerator and denominator
// Outputs: CoolBignum quotient
CoolBignumE operator/ (const CoolBignum& b1, const CoolBignum& b2) {
CoolBignum q,r; // Quotient and remainder
if (b2.count == 0) { // If b2 is zero
if (b1.count == 0) { // If b1 == zero
q.state = N_DIVIDE_BY_ZERO; // Both num & den are zero
b1.divide_by_zero("operator/ ()"); // Raise the exception
}
else if (b1.sign > 0) { // If b1 > zero
q.state = N_PLUS_INFINITY; // Positive infinity
b1.plus_infinity("operator/ ()"); // Raise the exception
}
else if (b1.sign < 0) { // If b1 < zero
q.state = N_MINUS_INFINITY; // Negative infinity
b1.minus_infinity("operator/ ()"); // Raise the exception
}
}
else {
divide(b1,b2,q,r); // Call divide fn
}
CoolBignumE& result = *((CoolBignumE*) &q); // same physical object
return result; // shallow swap on return
}
// operator% -- overload modulus operator for CoolBignums
// Inputs: references to CoolBignum number and modulus
// Outputs: CoolBignum modulus result
CoolBignumE operator% (const CoolBignum& b1, const CoolBignum& b2) {
CoolBignum q,r; // Temporary quotient and remainder
if (b2.count == 0) { // if b2 == 0
r.state = N_DIVIDE_BY_ZERO; // implies division by zero
b2.divide_by_zero("operator% ()"); // raise an exception
}
else { // otherwise,
divide(b1,b2,q,r); // divide b1 by b2 and save remainder
}
CoolBignumE& result = *((CoolBignumE*) &r); // same physical object
return result; // shallow swap on return
}
// left_shift -- left shift (arithmetic) CoolBignum by positive number.
// Inputs: reference to CoolBignum, positive shift value
// Outputs: CoolBignum, multiplied by the corresponding power of two
CoolBignumE left_shift (const CoolBignum& b1, long l) {
// to carry out this arithmetic left shift, we cheat. Instead of physically
// shifting the data array l bits to the left, we shift just enough to get
// the correct word alignment, and then pad the array on the right with as
// many zeros as we need.
CoolBignum rslt; // result of shift
rslt.sign = b1.sign; // result follows sign of input
Counter growth = Counter(l / data_bits_s); // # of words rslt will grow by
Data shift = Data(l % data_bits_s); // amount to actually shift
Data rshift = data_bits_s - shift; // amount to shift next word by
Data carry = // value that will be shifted
b1.data[b1.count - 1] >> (data_bits_s - shift); // out end of current array
rslt.data = // allocate new data array
new Data[rslt.count = b1.count + growth + (carry != 0 ? 1 : 0)];
Counter i = 0;
while (i < growth) // zero out padded elements
rslt.data[i++] = 0;
rslt.data[i++] = b1.data[0] << shift; // shift first non-zero element
while (i < rslt.count - 1) { // for remaining data words
rslt.data[i] = (b1.data[i - growth] << shift) + // shift current data word
(b1.data[i - 1 - growth] >> rshift); // propagate adjacent
i++; // carry into current word
}
if (i < rslt.count) {
if (carry) // if last word had overflow
rslt.data[i] = carry; // store it new data
else // otherwise,
rslt.data[i] = (b1.data[i - growth] << shift) + // do like the rest
(b1.data[i - 1 - growth] >> rshift);
}
CoolBignumE& result = *((CoolBignumE*) &rslt);// same physical object
return result; // shallow swap on return
}
// right_shift -- right shift (arithmetic) CoolBignum by positive number.
// Inputs: reference to CoolBignum, positive shift value
// Outputs: CoolBignum, divided by the corresponding power of two
CoolBignumE right_shift (const CoolBignum& b1, long l) {
CoolBignum rslt; // result of shift
Counter shrinkage = Counter(l / data_bits_s); // # of words rslt will shrink
Data shift = Data(l % data_bits_s); // amount to actually shift
Data dregs = (b1.data[b1.count - 1] >> shift); // high end data to save
if (shrinkage + (dregs == 0) < b1.count) { // if not all data shifted out
rslt.sign = b1.sign; // rslt follows sign of input
// allocate new data
rslt.data = new Data[rslt.count = b1.count - shrinkage
- (dregs == 0 ? 1 : 0)];
Data lshift = data_bits_s - shift; // amount to shift high word
Counter i = 0;
while (i < rslt.count - 1) { // shift current word
rslt.data[i] = (b1.data[i + shrinkage] >> shift) + // propagate adjacent
(b1.data[i + shrinkage + 1] << lshift); // word into current word
i++;
}
if (dregs) // don't lose dregs
rslt.data[i] = dregs;
else {
rslt.data[i] = (b1.data[i + shrinkage] >> shift) +
(b1.data[i + shrinkage + 1] << lshift);
}
}
CoolBignumE& result = *((CoolBignumE*) &rslt);// same physical object
return result; // shallow swap on return
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?