bignum.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 1,299 行 · 第 1/4 页

C
1,299
字号
  this->state = b.state;                        // Copy b's state
}



// operator= -- overloaded CoolBignum assignment operator
// Inputs:  reference to CoolBignum to be assigned
// Outputs:  reference to modified CoolBignum

CoolBignum& CoolBignum::operator= (const CoolBignum& b) {
  if (this != &b) {                             // So long as b is not "this"
    delete [] this->data;                       // Delete existing data
    this->count = b.count;                      // Copy b's count
    this->data =                                // Allocate data if necessary
      (this->count > 0 ? new Data[this->count] : 0);
    for (Counter i = 0; i < this->count; i++)   // Copy b's data
      this->data[i] = b.data[i];
    this->sign = b.sign;                        // Copy b's sign
    this->state = b.state;                      // Copy b's state
  }
  return *this;                                 // Return reference 
}



// operator- -- overloaded unary minus operator
// Inputs:  none
// Outputs:  CoolBignum, negated

CoolBignumE CoolBignum::operator- () const {
  CoolBignum temp(*this);                       // Temp stores result
  if (temp.count)                               // So long as this is non-zero
    temp.sign *= -1;                            // Flip its sign
  CoolBignumE& result = *((CoolBignumE*) &temp);// same physical object
  return result;                                // shallow swap on return
}


// operator++ overloaded increment operator for CoolBignums
// Inputs:  none
// Outputs:  reference to this CoolBignum, incremented

CoolBignum& CoolBignum::operator++ () {
  static CoolBignum one_s = 1l;                 // static CoolBignum equal to 1
  *this = *this + one_s;;                       // add one to this CoolBignum
  return *this;                                 // return reference to this B.
}



// operator--  overloaded decrement operator for CoolBignums
// Inputs:  none
// Outputs:  this CoolBignum, decremented

CoolBignum& CoolBignum::operator-- () {
  static CoolBignum one_s = 1l;                 // static CoolBignum equal to 1
  *this = *this - one_s;                        // add one to this CoolBignum
  return *this;                                 // return reference to this B.
}



// resize -- change the data allotment for a CoolBignum
// Inputs:  new size of data allotment
// Outputs:  none

void CoolBignum::resize (Counter new_count) {
  if (new_count != this->count) {               // If new size is really new
    Data *new_data =                            // Allocate data if necessary
      (new_count > 0 ? new Data[new_count] : 0);

    if (this->count <= new_count) {             // Copy old data into new
      Counter i;
      for (i = 0; i < this->count; i++)  
        new_data[i] = this->data[i];
      for (; i < new_count; i++)
        new_data[i] = 0;
    }
    else {                                     
      for (Counter i = 0; i < new_count; i++)  
        new_data[i] = this->data[i];
    }

    delete [] this->data;                       // Get rid of old data
    this->data = new_data;                      // Point to new data
    this->count = new_count;                    // Save new count
  }
}



// trim -- trim CoolBignum of excess data allotment
// Inputs:  none
// Outputs:  reference to modified CoolBignum

CoolBignum& CoolBignum::trim () {
  Counter i;
  for (i = this->count; i > 0; i--)             // Skip over high-order words
    if (this->data[i - 1] != 0) break;          //   that are zero
  if (i < this->count) {                        // If there are some such words
    Counter oldcount = this->count;
    this->count = i;                            // Update the count
    Data *new_data = (i > 0 ? new Data[i] : 0); // Allocate data if necessary
    for (; i > 0; i--)                          // Copy old data into new
      new_data[i - 1] = this->data[i - 1];
    delete [] this->data;                       // Delete old data
    this->data = new_data;                      // Point to new data
  }
  return *this;                                 // return reference to CoolBignum
}


// add -- add two CoolBignum values and save their sum
// Inputs:  references to two CoolBignum addends and the resulting sum
// Outputs:  none

void add (const CoolBignum& b1, const CoolBignum& b2, CoolBignum& sum) {
  const CoolBignum *bmax, *bmin;                        // Determine which of the two
  if (b1.count >= b2.count) {                   //   addends has the most
    bmax = &b1;                                 //   data.
    bmin = &b2;
  }
  else {
    bmax = &b2;
    bmin = &b1;
  }
  sum.data = ((sum.count = bmax->count) > 0 ?   // Allocate data for their sum
              new Data[sum.count] : 0);
  unsigned long temp, carry = 0;
  Counter i = 0;
  while (i < bmin->count) {                     // Add, element by element.
    // Add both elements and carry
    temp = (unsigned long)b1.data[i] + (unsigned long)b2.data[i] + carry;
    carry = temp/radix_s;                       // keep track of the carry
    sum.data[i] = Data(temp);                   // store sum
    i++;                                        // go to next element
  }
  while (i < bmax->count) {                     // bmin has no more elements
    temp = bmax->data[i] + carry;               // propagate the carry through
    carry = temp/radix_s;                       // the rest of bmax's elements
    sum.data[i] = Data(temp);                   // store sum
    i++;
  }
  if (carry) {                                  // if carry left over
    sum.resize(bmax->count + 1);                //   allocate another word
    sum.data[bmax->count] = 1;                  //   save the carry in it
  }
}



// subtract - subtract min CoolBignum from max CoolBignum (unsigned), result in diff
// Inputs:  references to CoolBignum
// Outputs:  none

void subtract (const CoolBignum& bmax, const CoolBignum& bmin, CoolBignum& diff) {
  diff.data = new Data[diff.count = bmax.count];// Allocate data for difference
  unsigned long temp;
  int borrow = 0;
  Counter i;
  for (i = 0; i < bmin.count; i++) {    // Subtract word by word.
    
    temp = (unsigned long)bmax.data[i] 
      + (unsigned long)radix_s - borrow;        // Add radix to bmax's data
    temp -= (unsigned long)bmin.data[i];        // Subtract off bmin's data
    borrow = (temp/radix_s == 0);               // Did we have to borrow?
    diff.data[i] = (Data) temp;                 // Reduce modulo radix and save
  }
  for (; i < bmax.count; i++) {                 // No more data for bmin
    temp = (unsigned long)bmax.data[i] 
      + (unsigned long)radix_s - borrow;        // Propagate the borrow through
    borrow = (temp/radix_s == 0);               //   rest of bmax's data
    diff.data[i] = (Data) temp;
  }
  diff.trim();                                  // Done. Now trim excess data
}



// magnitude_cmp - compare absolute values of two CoolBignums
// Inputs:  two CoolBignums
// Outputs:  result of comparison:  -1 if abs(b1) < abs(b2)
//                                   0 if abs(b1) == abs(b2)
//                                  +1 if abs(b1) > abs(b2)

int magnitude_cmp (const CoolBignum& b1, const CoolBignum& b2) {
  if (b1.count > b2.count) return 1;            // If one has more data than
  if (b2.count > b1.count) return -1;           //   the other, it wins
  Counter i = b1.count;                         // Else same number of elmts
  while (i > 0) {                               // Do lexicographic comparison
    if (b1.data[i - 1] > b2.data[i - 1])        
      return 1;                                 
    else if (b1.data[i - 1] < b2.data[i - 1])   
      return -1;
    i--;
  }                                             // No data, or all elmts same
  return 0;                                     //  so must be equal
}



// operator+ -- overloaded CoolBignum addition operator
// Inputs:  two references to CoolBignum addends
// Outputs:  new CoolBignum sum

CoolBignumE operator+ (const CoolBignum& b1, const CoolBignum& b2) {
  CoolBignum sum;                               // Init sum to zero
  if (b1.sign == b2.sign) {                     // If both have same sign
    add(b1,b2,sum);                             //   Do simple addition
    sum.sign = b1.sign;                         // Attach proper sign
  }
  else {                                        // Else different signs
    int mag = magnitude_cmp(b1,b2);             // Determine relative sizes
    if (mag > 0) {                              // If abs(b1) > abs(b2)
      subtract(b1,b2,sum);                      //   sum = b1 - b2
      sum.sign = b1.sign;                       // Sign of sum follows b1
    }
    else if (mag < 0) {                         // Else if abs(b1) < abs(b2)
      subtract(b2,b1,sum);                      //   sum = b2 - b1
      sum.sign = b2.sign;                       // Sign of sum follows b2
    }                                           // (Else abs(b1) == abs(b2)
  }                                             //   so sum must be zero)
  CoolBignumE& result = *((CoolBignumE*) &sum); // same physical object
  return result;                                // shallow swap on return
}


// multiply_aux -- multiply a CoolBignum by a "single digit"
// Inputs:  CoolBignum reference, single word multiplier, reference to the product,
//          and index of starting storage location to use in product
// Outputs:  none

void multiply_aux (const CoolBignum& b, Data d, CoolBignum& prod, Counter i) {
  // this function works just like normal multiplication by hand, in that the
  // top number is multiplied by the first digit of the bottom number, then the
  // second digit, and so on.  The only difference is that instead of doing all
  // of the multiplication before adding the rows, addition is done
  // concurrently.
  Counter j;
  if (i == 0) {                                 // if index is zero
    j = 0;                                      //   then zero out all of
    while (j < prod.count)                      //   prod's data elements
      prod.data[j++] = 0;                  
  }
  if (d != 0) {                                 // if d == 0, nothing to do
    unsigned long temp;
    Data carry = 0;

    Counter j;
    for (j = 0; j < b.count; j++) {     
      // for each of b's data elmts, multiply times d and add running product
      temp = (unsigned long)b.data[j] * (unsigned long)d
        + (unsigned long)prod.data[i + j] + carry;
      prod.data[i + j] = Data(temp % radix_s);  //   store result in product
      carry = Data(temp/radix_s);               //   keep track of carry
    }
    if (i+j < prod.count)
      prod.data[i + j] = carry;                 // Done.  Store the final carry
  }
}



// operator* -- overload * for CoolBignums
// Inputs:  references to two CoolBignum multiplicands
// Outputs:  CoolBignum product

CoolBignumE operator* (const CoolBignum& b1, const CoolBignum& b2) {
  CoolBignum prod;                              //   init product to zero
  if (b1 != zero_s && b2 != zero_s) {           // if neither multiplicand == 0
    prod.data =                                 //   allocate data for product
      new Data[prod.count = b1.count + b2.count];
    for (Counter i = 0; i < b2.count; i++)      //   multiply each b2 "digit" 
      multiply_aux(b1, b2.data[i], prod, i);    //   times b1 and add to total
    prod.sign = b1.sign * b2.sign;              //   determine correct sign
    prod.trim();                                //   trim excess data and ret.
  }
  CoolBignumE& result = *((CoolBignumE*) &prod);// same physical object
  return result;                                // shallow swap on return
}



// normalize -- normalize two CoolBignums  (Refer to Knuth, V.2, Section 4.3.1,
//              Algorithm D for details.  A digit here is one data element in
//              the radix 2**sizeof(Data).)
// Inputs:  references to two CoolBignums b1, b2, and their normalized counterparts
// Outputs:  the integral normalization factor used

Data normalize (const CoolBignum& b1, const CoolBignum& b2, CoolBignum& u, CoolBignum& v) {
  Data d =                                      // Calcualte normalization
    Data(radix_s/(((long)(b2.data[b2.count - 1])) + 1));        //   factor.
                //^^^^- JAM added this cast because Test#115 was overflowing resulting
                // in Divide Error because value was 65535 and wrapping to 0 with +1
  u.data = new Data[u.count = b1.count + 1];    // Get data for u (plus extra)
  v.data = new Data[v.count = b2.count];        // Get data for v
  u.data[b1.count] = 0;                         // Set u's leading digit to 0
  multiply_aux(b1,d,u,0);                       // u = b1 * d
  multiply_aux(b2,d,v,0);                       // v = b2 * d
  return d;                                     // return normalization factor
}



// divide_aux -- divide a CoolBignum by a "single digit" (Refer to Knuth, V.2,
//               Section 4.3.2, exercise 16 for details.  A digit here is one
//               data element in the radix 2**sizeof(Data).)
// Inputs:  reference to CoolBignum dividend, single digit divisor d, CoolBignum
//          quotient, and single digit remainder r
// Outputs:  none

void divide_aux (const CoolBignum& b1, Data d, CoolBignum& q, Data& r) {
  r = 0;                                        // init remainder to zero
  unsigned long temp;
  for (Counter j = b1.count; j > 0; j--) {
    temp = (unsigned long)r*radix_s 
      + (unsigned long)b1.data[j - 1];          // get remainder, append next
    if (j-1 < q.count) 
      q.data[j - 1] = Data(temp/d);             //   digit, then divide
    r = Data(temp % d);                         // calculate new remainder
  }
}


// estimate_q_hat -- estimate next dividend (Refer to Knuth, V.2, Section

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?