|
@@ -5,8 +5,8 @@
|
|
|
// Created by Sam Jaffe on 7/3/17.
|
|
// Created by Sam Jaffe on 7/3/17.
|
|
|
//
|
|
//
|
|
|
|
|
|
|
|
-#include "bigdecimal.h"
|
|
|
|
|
-#include "bignum_helper.h"
|
|
|
|
|
|
|
+#include "math/bigdecimal.h"
|
|
|
|
|
+#include "math/bignum_helper.h"
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
#include <cstdlib>
|
|
|
|
|
|
|
@@ -22,21 +22,22 @@ static size_t mul_scale(int32_t scale) {
|
|
|
|
|
|
|
|
static bool all_zero(bigdecimal::data_type const & data, size_t from) {
|
|
static bool all_zero(bigdecimal::data_type const & data, size_t from) {
|
|
|
for (size_t i = from; i > 0; --i) {
|
|
for (size_t i = from; i > 0; --i) {
|
|
|
- if (data[i-1] != 0) return false;
|
|
|
|
|
|
|
+ if (data[i - 1] != 0) return false;
|
|
|
}
|
|
}
|
|
|
return true;
|
|
return true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int compare(bigdecimal::data_type const & ldata, bigdecimal::data_type const & rdata, size_t offset) {
|
|
|
|
|
|
|
+static int compare(bigdecimal::data_type const & ldata,
|
|
|
|
|
+ bigdecimal::data_type const & rdata, size_t offset) {
|
|
|
auto cmp = detail::compare(ldata, rdata, offset);
|
|
auto cmp = detail::compare(ldata, rdata, offset);
|
|
|
if (cmp == 0) { return !all_zero(ldata, offset); }
|
|
if (cmp == 0) { return !all_zero(ldata, offset); }
|
|
|
return cmp;
|
|
return cmp;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static int compare(bigdecimal::data_type const & ldata, int32_t lsteps, bigdecimal::data_type const & rdata, int32_t rsteps) {
|
|
|
|
|
- return lsteps < rsteps ?
|
|
|
|
|
- -compare(rdata, ldata, size_t(rsteps - lsteps))
|
|
|
|
|
- : compare(ldata, rdata, size_t(lsteps - rsteps));
|
|
|
|
|
|
|
+static int compare(bigdecimal::data_type const & ldata, int32_t lsteps,
|
|
|
|
|
+ bigdecimal::data_type const & rdata, int32_t rsteps) {
|
|
|
|
|
+ return lsteps < rsteps ? -compare(rdata, ldata, size_t(rsteps - lsteps))
|
|
|
|
|
+ : compare(ldata, rdata, size_t(lsteps - rsteps));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static bool is_one(bigdecimal::data_type const & data, int32_t scale) {
|
|
static bool is_one(bigdecimal::data_type const & data, int32_t scale) {
|
|
@@ -44,28 +45,28 @@ static bool is_one(bigdecimal::data_type const & data, int32_t scale) {
|
|
|
return compare(data, add_scale(scale), ONE, 0) == 0;
|
|
return compare(data, add_scale(scale), ONE, 0) == 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void read(int32_t & dst, char const * & str, size_t len) {
|
|
|
|
|
- char seg[bigdecimal::SEG_DIGITS+1] = "";
|
|
|
|
|
|
|
+static void read(int32_t & dst, char const *& str, size_t len) {
|
|
|
|
|
+ char seg[bigdecimal::SEG_DIGITS + 1] = "";
|
|
|
strncpy(seg, str, len);
|
|
strncpy(seg, str, len);
|
|
|
dst = atoi(seg);
|
|
dst = atoi(seg);
|
|
|
str += len;
|
|
str += len;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-static void read_segment(bigdecimal::data_type & data, char const * number, size_t len) {
|
|
|
|
|
- auto elems = len/bigdecimal::SEG_DIGITS;
|
|
|
|
|
|
|
+static void read_segment(bigdecimal::data_type & data, char const * number,
|
|
|
|
|
+ size_t len) {
|
|
|
|
|
+ auto elems = len / bigdecimal::SEG_DIGITS;
|
|
|
data.resize(elems);
|
|
data.resize(elems);
|
|
|
- if (auto small = len-(elems*bigdecimal::SEG_DIGITS)) {
|
|
|
|
|
|
|
+ if (auto small = len - (elems * bigdecimal::SEG_DIGITS)) {
|
|
|
read(*data.emplace(data.end()), number, small);
|
|
read(*data.emplace(data.end()), number, small);
|
|
|
}
|
|
}
|
|
|
for (size_t idx = elems; idx > 0; --idx) {
|
|
for (size_t idx = elems; idx > 0; --idx) {
|
|
|
- read(data[idx-1], number, bigdecimal::SEG_DIGITS);
|
|
|
|
|
|
|
+ read(data[idx - 1], number, bigdecimal::SEG_DIGITS);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bigdecimal::bigdecimal() : is_negative(false), data({0}) {}
|
|
bigdecimal::bigdecimal() : is_negative(false), data({0}) {}
|
|
|
|
|
|
|
|
-bigdecimal::bigdecimal(bool neg, uint64_t value) :
|
|
|
|
|
-is_negative(neg) {
|
|
|
|
|
|
|
+bigdecimal::bigdecimal(bool neg, uint64_t value) : is_negative(neg) {
|
|
|
uint64_t next{0};
|
|
uint64_t next{0};
|
|
|
do {
|
|
do {
|
|
|
next = value / OVER_SEG;
|
|
next = value / OVER_SEG;
|
|
@@ -73,20 +74,19 @@ is_negative(neg) {
|
|
|
} while ((value = next) > 0);
|
|
} while ((value = next) > 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bigdecimal::bigdecimal(char const * number) :
|
|
|
|
|
-is_negative(number[0] == '-') {
|
|
|
|
|
|
|
+bigdecimal::bigdecimal(char const * number) : is_negative(number[0] == '-') {
|
|
|
if (is_negative) { ++number; }
|
|
if (is_negative) { ++number; }
|
|
|
if (auto p = strchr(number, '.')) {
|
|
if (auto p = strchr(number, '.')) {
|
|
|
read_segment(data, number, size_t(p - number));
|
|
read_segment(data, number, size_t(p - number));
|
|
|
number = p + 1;
|
|
number = p + 1;
|
|
|
set_scale(int32_t(strlen(number)));
|
|
set_scale(int32_t(strlen(number)));
|
|
|
- size_t elems = size_t(scale()/SEG_DIGITS);
|
|
|
|
|
|
|
+ size_t elems = size_t(scale() / SEG_DIGITS);
|
|
|
data.insert(data.begin(), elems, 0);
|
|
data.insert(data.begin(), elems, 0);
|
|
|
for (size_t idx = elems; idx > 0; --idx) {
|
|
for (size_t idx = elems; idx > 0; --idx) {
|
|
|
- read(data[idx-1], number, SEG_DIGITS);
|
|
|
|
|
|
|
+ read(data[idx - 1], number, SEG_DIGITS);
|
|
|
}
|
|
}
|
|
|
if (auto l = strlen(number)) {
|
|
if (auto l = strlen(number)) {
|
|
|
- data.insert(data.begin(), atoi(number) * detail::powers[SEG_DIGITS-l]);
|
|
|
|
|
|
|
+ data.insert(data.begin(), atoi(number) * detail::powers[SEG_DIGITS - l]);
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
read_segment(data, number, strlen(number));
|
|
read_segment(data, number, strlen(number));
|
|
@@ -136,17 +136,17 @@ void bigdecimal::subtract_impl(bigdecimal const & lhs, bool is_sub) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bigdecimal bigdecimal::operator-() const {
|
|
|
|
|
- return *this * NEGATIVE_ONE;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+bigdecimal bigdecimal::operator-() const { return *this * NEGATIVE_ONE; }
|
|
|
|
|
|
|
|
bigdecimal & math::operator+=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
bigdecimal & math::operator+=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
|
int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
|
|
int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
|
|
|
rhs.rescale(new_scale);
|
|
rhs.rescale(new_scale);
|
|
|
size_t const offset = size_t(rhs.steps_ - lhs.steps_);
|
|
size_t const offset = size_t(rhs.steps_ - lhs.steps_);
|
|
|
- if (lhs == bigdecimal::ZERO) { return rhs; }
|
|
|
|
|
- else if (rhs == bigdecimal::ZERO) { rhs.set_value(lhs); }
|
|
|
|
|
- else if (rhs.is_negative == lhs.is_negative) {
|
|
|
|
|
|
|
+ if (lhs == bigdecimal::ZERO) {
|
|
|
|
|
+ return rhs;
|
|
|
|
|
+ } else if (rhs == bigdecimal::ZERO) {
|
|
|
|
|
+ rhs.set_value(lhs);
|
|
|
|
|
+ } else if (rhs.is_negative == lhs.is_negative) {
|
|
|
detail::add(rhs.data, lhs.data, offset);
|
|
detail::add(rhs.data, lhs.data, offset);
|
|
|
} else {
|
|
} else {
|
|
|
rhs.subtract_impl(lhs, false);
|
|
rhs.subtract_impl(lhs, false);
|
|
@@ -158,9 +158,11 @@ bigdecimal & math::operator-=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
|
int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
|
|
int32_t const new_scale = std::max(rhs.scale(), lhs.scale());
|
|
|
rhs.rescale(new_scale);
|
|
rhs.rescale(new_scale);
|
|
|
size_t const offset = size_t(rhs.steps_ - lhs.steps_);
|
|
size_t const offset = size_t(rhs.steps_ - lhs.steps_);
|
|
|
- if (lhs == bigdecimal::ZERO) { return rhs; }
|
|
|
|
|
- else if (rhs == bigdecimal::ZERO) { rhs.set_value(-lhs); }
|
|
|
|
|
- else if (rhs.is_negative != lhs.is_negative) {
|
|
|
|
|
|
|
+ if (lhs == bigdecimal::ZERO) {
|
|
|
|
|
+ return rhs;
|
|
|
|
|
+ } else if (rhs == bigdecimal::ZERO) {
|
|
|
|
|
+ rhs.set_value(-lhs);
|
|
|
|
|
+ } else if (rhs.is_negative != lhs.is_negative) {
|
|
|
detail::add(rhs.data, lhs.data, offset);
|
|
detail::add(rhs.data, lhs.data, offset);
|
|
|
} else {
|
|
} else {
|
|
|
rhs.subtract_impl(lhs, true);
|
|
rhs.subtract_impl(lhs, true);
|
|
@@ -181,9 +183,7 @@ bigdecimal & math::operator*=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
|
} else {
|
|
} else {
|
|
|
detail::multiply(rhs.data, lhs.data);
|
|
detail::multiply(rhs.data, lhs.data);
|
|
|
auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
|
|
auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
|
|
|
- if (diff < 0) {
|
|
|
|
|
- rhs.data.erase(rhs.data.begin(), rhs.data.begin() - diff);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (diff < 0) { rhs.data.erase(rhs.data.begin(), rhs.data.begin() - diff); }
|
|
|
rhs.set_scale(new_scale);
|
|
rhs.set_scale(new_scale);
|
|
|
return rhs;
|
|
return rhs;
|
|
|
}
|
|
}
|
|
@@ -194,23 +194,24 @@ bigdecimal & math::operator*=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
|
bigdecimal & math::operator/=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
bigdecimal & math::operator/=(bigdecimal & rhs, bigdecimal const & lhs) {
|
|
|
int32_t const new_scale = rhs.scale() - lhs.scale();
|
|
int32_t const new_scale = rhs.scale() - lhs.scale();
|
|
|
rhs.is_negative ^= lhs.is_negative;
|
|
rhs.is_negative ^= lhs.is_negative;
|
|
|
- if (lhs == bigdecimal::ZERO) { throw std::domain_error("cannot divide by 0"); }
|
|
|
|
|
- else if (rhs == bigdecimal::ZERO) {
|
|
|
|
|
|
|
+ if (lhs == bigdecimal::ZERO) {
|
|
|
|
|
+ throw std::domain_error("cannot divide by 0");
|
|
|
|
|
+ } else if (rhs == bigdecimal::ZERO) {
|
|
|
rhs = bigdecimal::ZERO;
|
|
rhs = bigdecimal::ZERO;
|
|
|
} else if (!is_one(lhs.data, lhs.scale())) {
|
|
} else if (!is_one(lhs.data, lhs.scale())) {
|
|
|
if (rhs.scale() < new_scale) rhs.rescale(new_scale);
|
|
if (rhs.scale() < new_scale) rhs.rescale(new_scale);
|
|
|
auto cmp = detail::compare(rhs.data, lhs.data);
|
|
auto cmp = detail::compare(rhs.data, lhs.data);
|
|
|
- if (cmp < 0) { rhs = bigdecimal::ZERO; }
|
|
|
|
|
- else if (cmp == 0) { return rhs = {1, new_scale}; }
|
|
|
|
|
- else {
|
|
|
|
|
|
|
+ if (cmp < 0) {
|
|
|
|
|
+ rhs = bigdecimal::ZERO;
|
|
|
|
|
+ } else if (cmp == 0) {
|
|
|
|
|
+ return rhs = {1, new_scale};
|
|
|
|
|
+ } else {
|
|
|
rhs.data = detail::divide(rhs.data, lhs.data);
|
|
rhs.data = detail::divide(rhs.data, lhs.data);
|
|
|
auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
|
|
auto diff = add_scale(new_scale) - rhs.steps_ - lhs.steps_;
|
|
|
if (diff > 0) {
|
|
if (diff > 0) {
|
|
|
rhs.data.erase(rhs.data.begin(), rhs.data.begin() + diff);
|
|
rhs.data.erase(rhs.data.begin(), rhs.data.begin() + diff);
|
|
|
}
|
|
}
|
|
|
- if (rhs.data.size() <= mul_scale(new_scale)) {
|
|
|
|
|
- rhs.data.push_back(0);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (rhs.data.size() <= mul_scale(new_scale)) { rhs.data.push_back(0); }
|
|
|
rhs.set_scale(new_scale);
|
|
rhs.set_scale(new_scale);
|
|
|
return rhs;
|
|
return rhs;
|
|
|
}
|
|
}
|
|
@@ -236,7 +237,8 @@ bigdecimal math::operator/(bigdecimal rhs, bigdecimal const & lhs) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool math::operator==(bigdecimal const & lhs, bigdecimal const & rhs) {
|
|
bool math::operator==(bigdecimal const & lhs, bigdecimal const & rhs) {
|
|
|
- return lhs.is_negative == rhs.is_negative && compare(lhs.data, lhs.steps_, rhs.data, rhs.steps_) == 0;
|
|
|
|
|
|
|
+ return lhs.is_negative == rhs.is_negative &&
|
|
|
|
|
+ compare(lhs.data, lhs.steps_, rhs.data, rhs.steps_) == 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool math::operator!=(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
bool math::operator!=(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
@@ -247,43 +249,47 @@ bool math::operator<=(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
return !(rhs > lhs);
|
|
return !(rhs > lhs);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool math::operator< (bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
|
|
- if (rhs.is_negative != lhs.is_negative) { return rhs.is_negative; }
|
|
|
|
|
- else if (rhs.is_negative) { return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) > 0; }
|
|
|
|
|
- else { return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) < 0; }
|
|
|
|
|
|
|
+bool math::operator<(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
|
|
+ if (rhs.is_negative != lhs.is_negative) {
|
|
|
|
|
+ return rhs.is_negative;
|
|
|
|
|
+ } else if (rhs.is_negative) {
|
|
|
|
|
+ return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) > 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return compare(rhs.data, rhs.steps_, lhs.data, lhs.steps_) < 0;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool math::operator>=(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
bool math::operator>=(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
return !(rhs < lhs);
|
|
return !(rhs < lhs);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bool math::operator> (bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
|
|
|
|
+bool math::operator>(bigdecimal const & rhs, bigdecimal const & lhs) {
|
|
|
return lhs < rhs;
|
|
return lhs < rhs;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1}, bigdecimal::NEGATIVE_ONE{-1};
|
|
|
|
|
|
|
+bigdecimal const bigdecimal::ZERO{0}, bigdecimal::ONE{1},
|
|
|
|
|
+ bigdecimal::NEGATIVE_ONE{-1};
|
|
|
|
|
|
|
|
std::string bigdecimal::to_string() const {
|
|
std::string bigdecimal::to_string() const {
|
|
|
size_t const decimal_split = size_t(std::max(0, steps_));
|
|
size_t const decimal_split = size_t(std::max(0, steps_));
|
|
|
- int32_t const hidden = std::max(0, SEG_DIGITS * (-scale()/SEG_DIGITS));
|
|
|
|
|
|
|
+ int32_t const hidden = std::max(0, SEG_DIGITS * (-scale() / SEG_DIGITS));
|
|
|
size_t const chars = SEG_DIGITS * data.size() + size_t(hidden);
|
|
size_t const chars = SEG_DIGITS * data.size() + size_t(hidden);
|
|
|
std::vector<char> output(chars + 3, '\0');
|
|
std::vector<char> output(chars + 3, '\0');
|
|
|
char * ptr = output.data();
|
|
char * ptr = output.data();
|
|
|
if (is_negative) { *ptr++ = '-'; }
|
|
if (is_negative) { *ptr++ = '-'; }
|
|
|
ptr += sprintf(ptr, "%d", data.back());
|
|
ptr += sprintf(ptr, "%d", data.back());
|
|
|
- for (size_t i = data.size()-1; i > decimal_split; --i) {
|
|
|
|
|
- ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i-1]);
|
|
|
|
|
|
|
+ for (size_t i = data.size() - 1; i > decimal_split; --i) {
|
|
|
|
|
+ ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i - 1]);
|
|
|
}
|
|
}
|
|
|
if (scale() > 0) {
|
|
if (scale() > 0) {
|
|
|
*ptr++ = '.';
|
|
*ptr++ = '.';
|
|
|
for (size_t i = decimal_split; i > 1; --i) {
|
|
for (size_t i = decimal_split; i > 1; --i) {
|
|
|
- ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i-1]);
|
|
|
|
|
|
|
+ ptr += sprintf(ptr, "%0.*d", SEG_DIGITS, data[i - 1]);
|
|
|
}
|
|
}
|
|
|
int32_t const val = scale() % SEG_DIGITS;
|
|
int32_t const val = scale() % SEG_DIGITS;
|
|
|
- sprintf(ptr, "%0.*d", val, data[0]/detail::powers[SEG_DIGITS-val]);
|
|
|
|
|
|
|
+ sprintf(ptr, "%0.*d", val, data[0] / detail::powers[SEG_DIGITS - val]);
|
|
|
} else if (data.back()) {
|
|
} else if (data.back()) {
|
|
|
sprintf(ptr, "%0.*d", hidden, 0);
|
|
sprintf(ptr, "%0.*d", hidden, 0);
|
|
|
}
|
|
}
|
|
|
return output.data();
|
|
return output.data();
|
|
|
}
|
|
}
|
|
|
-
|
|
|