bignum_helper.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. //
  2. // bignum_helper.cpp
  3. // bigdecimal
  4. //
  5. // Created by Sam Jaffe on 7/4/17.
  6. //
  7. #include "bignum_helper.h"
  8. #include <cmath>
  9. using namespace math::detail;
  10. namespace {
  11. constexpr int32_t const MAX_SEG { 999999999};
  12. constexpr int32_t const OVER_SEG{1000000000};
  13. constexpr int32_t const SEG_DIGITS{9};
  14. }
  15. namespace math { namespace detail {
  16. #define IMPL_COMPARE(expr) \
  17. if (rhs expr < lhs expr) return -1; \
  18. else if (lhs expr < rhs expr) return 1
  19. int compare(data_type const & rhs, data_type const & lhs) {
  20. IMPL_COMPARE(.size());
  21. for (size_t i = rhs.size(); i > 0; --i) {
  22. IMPL_COMPARE([i-1]);
  23. }
  24. return 0;
  25. }
  26. #undef IMPL_COMPARE
  27. void add(data_type & rhs, data_type const & lhs, size_t offset) {
  28. rhs.resize(std::max(rhs.size(), lhs.size()+offset)+1);
  29. auto const lbnd = lhs.size(), ubnd = rhs.size() - 1;
  30. // Add
  31. for (size_t i = 0; i < lbnd; ++i) {
  32. rhs[i+offset] += lhs[i];
  33. }
  34. // Carry
  35. for (size_t i = 0; i < ubnd; ++i) {
  36. if (rhs[i] > MAX_SEG) {
  37. rhs[i] -= OVER_SEG;
  38. rhs[i+1] += 1;
  39. }
  40. }
  41. if (rhs[ubnd] == 0) { rhs.pop_back(); }
  42. }
  43. void subtract_nounderflow(data_type & rhs, data_type const & lhs, size_t offset) {
  44. size_t const rbnd = rhs.size(), lbnd = lhs.size();
  45. // Subtract
  46. for (size_t i = 0; i < lbnd; ++i) {
  47. rhs[i+offset] -= lhs[i];
  48. }
  49. // Borrow
  50. for (size_t i = 0; i < rbnd; ++i) {
  51. if (rhs[i] < 0) {
  52. rhs[i] += OVER_SEG;
  53. rhs[i+1] -= 1;
  54. }
  55. }
  56. if (rhs[rbnd-1] == 0 && rbnd > 1) { rhs.pop_back(); }
  57. }
  58. void multiply(data_type & rhs, data_type const & lhs) {
  59. size_t const rbnd = rhs.size(), lbnd = lhs.size();
  60. size_t const ubnd = rbnd + lbnd;
  61. rhs.resize(ubnd);
  62. // Multiply
  63. for (size_t i = rbnd; i > 0; --i) {
  64. int32_t const value = rhs[i-1];
  65. for (size_t j = lbnd; j > 0; --j) {
  66. // Max input 999,999,999
  67. // Max output 999,999,998,000,000,001
  68. int64_t product = static_cast<int64_t>(value) * static_cast<int64_t>(lhs[j-1]);
  69. int64_t overflow = product / OVER_SEG;
  70. rhs[i+j-2] += static_cast<int32_t>(product - (overflow * OVER_SEG));
  71. rhs[i+j-1] += static_cast<int32_t>(overflow);
  72. }
  73. rhs[i-1] -= value;
  74. }
  75. // Carry
  76. for (size_t i = 0; i < ubnd-1; ++i) {
  77. if (rhs[i] > MAX_SEG) {
  78. int32_t overflow = rhs[i] / OVER_SEG;
  79. rhs[i] -= (overflow * OVER_SEG);
  80. rhs[i+1] += overflow;
  81. }
  82. }
  83. while (rhs.back() == 0) { rhs.pop_back(); }
  84. }
  85. data_type shift10(data_type const & data, int32_t pow, size_t shift) {
  86. size_t const bnd = data.size();
  87. data_type rval(bnd + shift + 1);
  88. for (size_t i = 0; i < bnd; ++i) {
  89. int64_t product = static_cast<int64_t>(data[i]) * static_cast<int64_t>(pow);
  90. int64_t overflow = product / OVER_SEG;
  91. rval[i+shift] += static_cast<int32_t>(product - (overflow * OVER_SEG));
  92. rval[i+shift+1] += static_cast<int32_t>(overflow);
  93. }
  94. if (rval.back() == 0 && rval.size() > 1) { rval.pop_back(); }
  95. return rval;
  96. }
  97. size_t digits(int32_t val) {
  98. return val == 0 ? 1 : static_cast<size_t>(floor(log10(val))) + 1;
  99. }
  100. size_t digits(data_type const & data) {
  101. return SEG_DIGITS * (data.size()-1) + digits(data.back());
  102. }
  103. int32_t powers[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
  104. data_type divide(data_type & remainder, data_type const & divisor) {
  105. data_type accum{0};
  106. auto const dig = digits(divisor);
  107. do {
  108. auto const diff = std::max(1UL, digits(remainder) - dig) - 1;
  109. auto const shift = diff / SEG_DIGITS;
  110. auto const ipow = diff - (shift * SEG_DIGITS);
  111. data_type step{shift10({1}, powers[ipow], 0)};
  112. data_type value{shift10(divisor, powers[ipow], 0)};
  113. do {
  114. subtract_nounderflow(remainder, value, shift);
  115. add(accum, step, shift);
  116. } while (compare(remainder, value) >= 0);
  117. } while (compare(remainder, divisor) >= 0);
  118. return accum;
  119. }
  120. } }