bigdecimal.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //
  2. // bigdecimal.cpp
  3. // bigdecimal
  4. //
  5. // Created by Sam Jaffe on 7/3/17.
  6. //
  7. #include "math/bigdecimal.h"
  8. #include <cstdlib>
  9. #include "bignum_helper.h"
  10. using namespace math;
  11. static int32_t add_scale(int32_t scale) {
  12. return scale / bigdecimal::SEG_DIGITS + (scale > 0 ? 1 : 0);
  13. }
  14. static size_t mul_scale(int32_t scale) {
  15. return scale < 0 ? 0 : size_t(add_scale(scale));
  16. }
  17. static bool all_zero(bigdecimal::data_type const & data, size_t from) {
  18. for (size_t i = from; i > 0; --i) {
  19. if (data[i - 1] != 0) return false;
  20. }
  21. return true;
  22. }
  23. static int compare(bigdecimal::data_type const & ldata,
  24. bigdecimal::data_type const & rdata, size_t offset) {
  25. auto cmp = detail::compare(ldata, rdata, offset);
  26. if (cmp == 0) { return !all_zero(ldata, offset); }
  27. return cmp;
  28. }
  29. static int compare(bigdecimal::data_type const & ldata, int32_t lsteps,
  30. bigdecimal::data_type const & rdata, int32_t rsteps) {
  31. return lsteps < rsteps ? -compare(rdata, ldata, size_t(rsteps - lsteps))
  32. : compare(ldata, rdata, size_t(lsteps - rsteps));
  33. }
  34. static bool is_one(bigdecimal::data_type const & data, int32_t scale) {
  35. static bigdecimal::data_type ONE = {1};
  36. return compare(data, add_scale(scale), ONE, 0) == 0;
  37. }
  38. static void read(int32_t & dst, char const *& str, size_t len) {
  39. char seg[bigdecimal::SEG_DIGITS + 1] = "";
  40. strncpy(seg, str, len);
  41. dst = atoi(seg);
  42. str += len;
  43. }
  44. static void read_segment(bigdecimal::data_type & data, char const * number,
  45. size_t len) {
  46. auto elems = len / bigdecimal::SEG_DIGITS;
  47. data.resize(elems);
  48. if (auto small = len - (elems * bigdecimal::SEG_DIGITS)) {
  49. read(*data.emplace(data.end()), number, small);
  50. }
  51. for (size_t idx = elems; idx > 0; --idx) {
  52. read(data[idx - 1], number, bigdecimal::SEG_DIGITS);
  53. }
  54. }
  55. bigdecimal::bigdecimal() : is_negative(false), data({0}) {}
  56. bigdecimal::bigdecimal(bool neg, uint64_t value) : is_negative(neg) {
  57. uint64_t next{0};
  58. do {
  59. next = value / OVER_SEG;
  60. data.push_back(static_cast<int32_t>(value - (OVER_SEG * next)));
  61. } while ((value = next) > 0);
  62. }
  63. bigdecimal::bigdecimal(char const * number) : is_negative(number[0] == '-') {
  64. if (is_negative) { ++number; }
  65. if (auto p = strchr(number, '.')) {
  66. read_segment(data, number, size_t(p - number));
  67. number = p + 1;
  68. set_scale(int32_t(strlen(number)));
  69. size_t elems = size_t(scale() / SEG_DIGITS);
  70. data.insert(data.begin(), elems, 0);
  71. for (size_t idx = elems; idx > 0; --idx) {
  72. read(data[idx - 1], number, SEG_DIGITS);
  73. }
  74. if (auto l = strlen(number)) {
  75. data.insert(data.begin(), atoi(number) * detail::powers[SEG_DIGITS - l]);
  76. }
  77. } else {
  78. read_segment(data, number, strlen(number));
  79. }
  80. }
  81. void bigdecimal::set_value(bigdecimal const & other) {
  82. int32_t oscale = scale();
  83. operator=(other);
  84. rescale(oscale);
  85. }
  86. void bigdecimal::set_scale(int32_t nscale) {
  87. scale_ = nscale;
  88. steps_ = add_scale(nscale);
  89. }
  90. void bigdecimal::rescale(int32_t nscale) {
  91. auto const diff = steps_ - add_scale(nscale);
  92. if (diff > 0) {
  93. data.erase(data.begin(), data.begin() + diff);
  94. if (data.empty()) { data.push_back(0); }
  95. } else if (diff < 0) {
  96. data.insert(data.begin(), size_t(-diff), 0);
  97. }
  98. auto const idx = -nscale % SEG_DIGITS;
  99. if (scale() > nscale && nscale < 0 && idx) {
  100. data.front() /= detail::powers[idx];
  101. data.front() *= detail::powers[idx];
  102. }
  103. set_scale(nscale);
  104. }
  105. void bigdecimal::subtract_impl(bigdecimal const & lhs, bool is_sub) {
  106. size_t const offset = size_t(steps_ - lhs.steps_);
  107. auto cmp = detail::compare(data, lhs.data, offset);
  108. if (cmp == 0) {
  109. set_value(bigdecimal::ZERO);
  110. } else if (cmp > 0) {
  111. detail::subtract_nounderflow(data, lhs.data, offset);
  112. } else {
  113. bigdecimal tmp{lhs};
  114. tmp.is_negative = lhs.is_negative ^ is_sub;
  115. swap(*this, tmp);
  116. rescale(tmp.scale());
  117. detail::subtract_nounderflow(data, tmp.data);
  118. }
  119. }
  120. bigdecimal bigdecimal::operator-() const { return *this * NEGATIVE_ONE; }
  121. bigdecimal & math::operator+=(bigdecimal & rhs, bigdecimal const & lhs) {
  122. int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
  123. rhs.rescale(new_scale);
  124. size_t const offset = size_t(rhs.steps_ - lhs.steps_);
  125. if (lhs == bigdecimal::ZERO) {
  126. return rhs;
  127. } else if (rhs == bigdecimal::ZERO) {
  128. rhs.set_value(lhs);
  129. } else if (rhs.is_negative == lhs.is_negative) {
  130. detail::add(rhs.data, lhs.data, offset);
  131. } else {
  132. rhs.subtract_impl(lhs, false);
  133. }
  134. return rhs;
  135. }
  136. bigdecimal & math::operator-=(bigdecimal & rhs, bigdecimal const & lhs) {
  137. int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
  138. rhs.rescale(new_scale);
  139. size_t const offset = size_t(rhs.steps_ - lhs.steps_);
  140. if (lhs == bigdecimal::ZERO) {
  141. return rhs;
  142. } else if (rhs == bigdecimal::ZERO) {
  143. rhs.set_value(-lhs);
  144. } else if (rhs.is_negative != lhs.is_negative) {
  145. detail::add(rhs.data, lhs.data, offset);
  146. } else {
  147. rhs.subtract_impl(lhs, true);
  148. }
  149. return rhs;
  150. }
  151. bigdecimal & math::operator*=(bigdecimal & rhs, bigdecimal const & lhs) {
  152. int32_t const new_scale = rhs.scale() + lhs.scale();
  153. bool is_neg = rhs.is_negative != lhs.is_negative;
  154. if (rhs == bigdecimal::ZERO || lhs == bigdecimal::ZERO) {
  155. rhs = bigdecimal::ZERO;
  156. } else if (is_one(lhs.data, lhs.scale())) {
  157. rhs.is_negative = is_neg;
  158. } else if (is_one(rhs.data, rhs.scale())) {
  159. rhs = lhs;
  160. rhs.is_negative = is_neg;
  161. } else {
  162. detail::multiply(rhs.data, lhs.data);
  163. auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
  164. if (diff < 0) { rhs.data.erase(rhs.data.begin(), rhs.data.begin() - diff); }
  165. rhs.set_scale(new_scale);
  166. return rhs;
  167. }
  168. rhs.rescale(new_scale);
  169. return rhs;
  170. }
  171. bigdecimal & math::operator/=(bigdecimal & rhs, bigdecimal const & lhs) {
  172. int32_t const new_scale = rhs.scale() - lhs.scale();
  173. rhs.is_negative ^= lhs.is_negative;
  174. if (lhs == bigdecimal::ZERO) {
  175. throw std::domain_error("cannot divide by 0");
  176. } else if (rhs == bigdecimal::ZERO) {
  177. rhs = bigdecimal::ZERO;
  178. } else if (!is_one(lhs.data, lhs.scale())) {
  179. if (rhs.scale() < new_scale) rhs.rescale(new_scale);
  180. auto cmp = detail::compare(rhs.data, lhs.data);
  181. if (cmp < 0) {
  182. rhs = bigdecimal::ZERO;
  183. } else if (cmp == 0) {
  184. return rhs = {1, new_scale};
  185. } else {
  186. rhs.data = detail::divide(rhs.data, lhs.data);
  187. auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
  188. if (diff > 0) {
  189. rhs.data.erase(rhs.data.begin(), rhs.data.begin() + diff);
  190. }
  191. if (rhs.data.size() <= mul_scale(new_scale)) { rhs.data.push_back(0); }
  192. rhs.set_scale(new_scale);
  193. return rhs;
  194. }
  195. }
  196. rhs.rescale(new_scale);
  197. return rhs;
  198. }
  199. bigdecimal math::operator+(bigdecimal rhs, bigdecimal const & lhs) {
  200. return rhs += lhs;
  201. }
  202. bigdecimal math::operator-(bigdecimal rhs, bigdecimal const & lhs) {
  203. return rhs -= lhs;
  204. }
  205. bigdecimal math::operator*(bigdecimal rhs, bigdecimal const & lhs) {
  206. return rhs *= lhs;
  207. }
  208. bigdecimal math::operator/(bigdecimal rhs, bigdecimal const & lhs) {
  209. return rhs /= lhs;
  210. }
  211. bool math::operator==(bigdecimal const & lhs, bigdecimal const & rhs) {
  212. return lhs.is_negative == rhs.is_negative &&
  213. compare(lhs.data, lhs.steps_, rhs.data, rhs.steps_) == 0;
  214. }
  215. bool math::operator!=(bigdecimal const & rhs, bigdecimal const & lhs) {
  216. return !(rhs == lhs);
  217. }
  218. bool math::operator<=(bigdecimal const & rhs, bigdecimal const & lhs) {
  219. return !(rhs > lhs);
  220. }
  221. bool math::operator<(bigdecimal const & rhs, bigdecimal const & lhs) {
  222. if (rhs.is_negative != lhs.is_negative) {
  223. return rhs.is_negative;
  224. } else if (rhs.is_negative) {
  225. return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) > 0;
  226. } else {
  227. return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) < 0;
  228. }
  229. }
  230. bool math::operator>=(bigdecimal const & rhs, bigdecimal const & lhs) {
  231. return !(rhs < lhs);
  232. }
  233. bool math::operator>(bigdecimal const & rhs, bigdecimal const & lhs) {
  234. return lhs < rhs;
  235. }
  236. bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1},
  237. bigdecimal::NEGATIVE_ONE{-1};
  238. std::string bigdecimal::to_string(number_format const & fmt) const {
  239. size_t const decimal_split = size_t(std::max(0, steps_));
  240. int32_t const hidden = std::max(0, SEG_DIGITS * (-scale() / SEG_DIGITS));
  241. size_t const chars = SEG_DIGITS * data.size() + size_t(hidden);
  242. std::vector<char> output(chars + 3, '\0');
  243. char * ptr = output.data();
  244. if (is_negative) { *ptr++ = '-'; }
  245. ptr += sprintf(ptr, "%d", data.back());
  246. for (size_t i = data.size() - 1; i > decimal_split; --i) {
  247. ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i - 1]);
  248. }
  249. if (scale() > 0) {
  250. *ptr++ = '.';
  251. for (size_t i = decimal_split; i > 1; --i) {
  252. ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i - 1]);
  253. }
  254. int32_t const val = scale() % SEG_DIGITS;
  255. sprintf(ptr, "%0.*d", val, data[0] / detail::powers[SEG_DIGITS - val]);
  256. } else if (data.back()) {
  257. sprintf(ptr, "%0.*d", hidden, 0);
  258. }
  259. return detail::apply(fmt, output.data());
  260. }