فهرست منبع

Adding division using the multiply-and-subtract method.
Adding test cases for division.

Samuel Jaffe 8 سال پیش
والد
کامیت
9cc561ad19
2فایلهای تغییر یافته به همراه99 افزوده شده و 4 حذف شده
  1. 59 4
      src/biginteger.cpp
  2. 40 0
      test/biginteger.t.h

+ 59 - 4
src/biginteger.cpp

@@ -21,6 +21,7 @@ namespace detail {
   data_type add(data_type rhs, data_type const & lhs);
   data_type subtract_nounderflow(data_type rhs, data_type const & lhs);
   data_type multiply(data_type const & rhs, data_type const & lhs);
+  data_type divide(data_type remainder, data_type const & divisor);
 }
 
 static void swap(biginteger & rhs, biginteger && lhs) {
@@ -92,10 +93,10 @@ biginteger & operator*=(biginteger & rhs, biginteger const & lhs) {
   return rhs;
 }
 
-//biginteger & operator/=(biginteger & rhs, biginteger const & lhs) {
-//  swap(rhs, rhs / lhs);
-//  return rhs;
-//}
+biginteger & operator/=(biginteger & rhs, biginteger const & lhs) {
+  swap(rhs, rhs / lhs);
+  return rhs;
+}
 
 biginteger biginteger::operator-() const {
   return (*this) * NEGATIVE_ONE;
@@ -142,6 +143,20 @@ biginteger math::operator*(biginteger const & rhs, biginteger const & lhs) {
   return {rhs.is_negative != lhs.is_negative, detail::multiply(rhs.data, lhs.data)};
 }
 
+biginteger math::operator/(biginteger const & rhs, biginteger const & lhs) {
+  if (lhs == biginteger::ZERO) { throw 0; }
+  else if (rhs == biginteger::ZERO) { return biginteger::ZERO; }
+  else if (detail::compare(lhs.data, {1}) == 0) {
+    return {rhs.is_negative != lhs.is_negative, biginteger::data_type{rhs.data}};
+  } else {
+    auto cmp = detail::compare(rhs.data, lhs.data);
+    bool is_neg = rhs.is_negative != lhs.is_negative;
+    if (cmp < 0) { return biginteger::ZERO; }
+    else if (cmp == 0) { return {is_neg, {1}}; }
+    else { return {is_neg, detail::divide(rhs.data, lhs.data)}; }
+  }
+}
+
 namespace detail {
 #define IMPL_COMPARE(expr) \
   if (rhs expr < lhs expr) return -1; \
@@ -227,6 +242,46 @@ namespace detail {
     while (rval.back() == 0 && rval.size() > 1) { rval.pop_back(); }
     return rval;
   }
+  
+  data_type shift10(data_type const & data, int32_t pow, size_t shift) {
+    size_t const bnd = data.size();
+    data_type rval(bnd + shift + 1);
+    for (size_t i = 0; i < bnd; ++i) {
+      int64_t product = static_cast<int64_t>(data[i]) * static_cast<int64_t>(pow);
+      int64_t overflow = product / biginteger::OVER_SEG;
+      rval[i+shift] += static_cast<int32_t>(product - (overflow * biginteger::OVER_SEG));
+      rval[i+shift+1] += static_cast<int32_t>(overflow);
+    }
+    if (rval.back() == 0 && rval.size() > 1) { rval.pop_back(); }
+    return rval;
+  }
+  
+  size_t digits(int32_t val) {
+    return val == 0 ? 1 : static_cast<size_t>(floor(log10(val))) + 1;
+  }
+  
+  size_t digits(data_type const & data) {
+    return biginteger::SEG_DIGITS * (data.size()-1) + digits(data.back());
+  }
+  
+  int32_t powers[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+  
+  data_type divide(data_type remainder, data_type const & divisor) {
+    data_type accum{0};
+    auto const dig = digits(divisor);
+    while (detail::compare(remainder, divisor) >= 0) {
+      auto const diff = digits(remainder) - dig - 1;
+      auto const shift = diff / biginteger::SEG_DIGITS;
+      auto const ipow = diff - (shift * biginteger::SEG_DIGITS);
+      data_type step{shift10({1}, powers[ipow], shift)};
+      data_type value{shift10(divisor, powers[ipow], shift)};
+      do {
+        subtract_from(remainder, value);
+        add_into(accum, step);
+      } while (detail::compare(remainder, value) >= 0);
+    }
+    return accum;
+  }
 }
 
 bool math::operator==(biginteger const & rhs, biginteger const & lhs) {

+ 40 - 0
test/biginteger.t.h

@@ -95,4 +95,44 @@ public:
     math::biginteger bi{1000};
     TS_ASSERT_EQUALS((bi*bi).to_string(), "1000000");
   }
+  
+  void testDivideByZeroThrows() {
+    auto &ZERO =  math::biginteger::ZERO;
+    math::biginteger bi{1000};
+    TS_ASSERT_THROWS(bi/ZERO, int);
+  }
+  
+  void testDivideByOneReturnsValue() {
+    auto &ONE =  math::biginteger::ONE;
+    math::biginteger bi{1000};
+    TS_ASSERT_EQUALS((bi/ONE).to_string(), "1000");
+  }
+
+  void testDivideByNegativeOneReturnsInverse() {
+    auto &NEGATIVE_ONE =  math::biginteger::NEGATIVE_ONE;
+    math::biginteger bi{1000};
+    TS_ASSERT_EQUALS((bi/NEGATIVE_ONE).to_string(), "-1000");
+  }
+
+  void testDivisionWithMultipleMultSubSteps() {
+    math::biginteger bi{1112};
+    TS_ASSERT_EQUALS((bi/2).to_string(), "556");
+  }
+  
+  void testDivisionDroppingNumberOfCells() {
+    math::biginteger bi{1000000000ULL};
+    TS_ASSERT_EQUALS((bi/2).to_string(), "500000000");
+  }
+  
+  void testDivisionByBiggerNumberIsZero() {
+    math::biginteger bi{1000ULL};
+    TS_ASSERT_EQUALS((bi/1001).to_string(), "0");
+  }
+  
+  void testDivisionWithLargeNumbers() {
+    math::biginteger big{"999999998000000001"};
+    TS_ASSERT_EQUALS(big.to_string(), "999999998000000001");
+    TS_ASSERT_EQUALS((big/999999999ULL).to_string(),
+                     "999999999");
+  }
 };