SHA256
342 lines
8.3 KiB
C++
342 lines
8.3 KiB
C++
#include <bstl/big_decimal.hpp>
|
|
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
#include <cmath>
|
|
|
|
namespace bstl {
|
|
|
|
BigDecimal::BigDecimal() : unscaledValue_(0), scale_(0) {}
|
|
|
|
BigDecimal::BigDecimal(int value) : unscaledValue_(value), scale_(0) {}
|
|
|
|
BigDecimal::BigDecimal(long long value) : unscaledValue_(value), scale_(0) {}
|
|
|
|
BigDecimal::BigDecimal(double value) : scale_(0) {
|
|
if (value == 0.0) {
|
|
unscaledValue_ = 0;
|
|
scale_ = 0;
|
|
return;
|
|
}
|
|
|
|
// 将浮点数转换为字符串,然后解析
|
|
std::ostringstream oss;
|
|
oss.precision(15);
|
|
oss << value;
|
|
*this = BigDecimal(oss.str());
|
|
}
|
|
|
|
BigDecimal::BigDecimal(const std::string& value) : unscaledValue_(0), scale_(0) {
|
|
if (value.empty()) {
|
|
return;
|
|
}
|
|
|
|
std::string text = value;
|
|
|
|
// 去掉前后空格
|
|
size_t start = 0, end = text.size();
|
|
while (start < end && text[start] == ' ') start++;
|
|
while (end > start && text[end - 1] == ' ') end--;
|
|
text = text.substr(start, end - start);
|
|
|
|
if (text.empty()) {
|
|
return;
|
|
}
|
|
|
|
// 查找小数点位置
|
|
size_t dotPos = text.find('.');
|
|
if (dotPos != std::string::npos) {
|
|
scale_ = text.size() - dotPos - 1;
|
|
std::string digits = text.substr(0, dotPos) + text.substr(dotPos + 1);
|
|
unscaledValue_ = BigInteger(digits);
|
|
} else {
|
|
scale_ = 0;
|
|
unscaledValue_ = BigInteger(text);
|
|
}
|
|
}
|
|
|
|
BigDecimal::BigDecimal(const char* value)
|
|
: BigDecimal(value == nullptr ? std::string("0") : std::string(value)) {}
|
|
|
|
BigDecimal::BigDecimal(const BigInteger& unscaledValue, int scale)
|
|
: unscaledValue_(unscaledValue), scale_(scale) {
|
|
if (scale < 0) {
|
|
throw std::invalid_argument("scale cannot be negative");
|
|
}
|
|
}
|
|
|
|
bool BigDecimal::isZero() const {
|
|
return unscaledValue_.isZero();
|
|
}
|
|
|
|
int BigDecimal::sign() const {
|
|
return unscaledValue_.sign();
|
|
}
|
|
|
|
int BigDecimal::getScale() const {
|
|
return scale_;
|
|
}
|
|
|
|
BigInteger BigDecimal::getUnscaledValue() const {
|
|
return unscaledValue_;
|
|
}
|
|
|
|
std::string BigDecimal::toString() const {
|
|
if (isZero()) {
|
|
return "0";
|
|
}
|
|
|
|
std::string digitStr = unscaledValue_.toString();
|
|
|
|
// 处理符号
|
|
bool negative = false;
|
|
if (digitStr[0] == '-') {
|
|
negative = true;
|
|
digitStr = digitStr.substr(1);
|
|
}
|
|
|
|
// 插入小数点
|
|
if (scale_ == 0) {
|
|
return (negative ? "-" : "") + digitStr;
|
|
}
|
|
|
|
if (static_cast<int>(digitStr.size()) <= scale_) {
|
|
// 需要前置 "0."
|
|
std::string result = "0.";
|
|
for (int i = 0; i < scale_ - static_cast<int>(digitStr.size()); i++) {
|
|
result += "0";
|
|
}
|
|
result += digitStr;
|
|
return (negative ? "-" : "") + result;
|
|
}
|
|
|
|
int dotPos = digitStr.size() - scale_;
|
|
std::string result = digitStr.substr(0, dotPos) + "." + digitStr.substr(dotPos);
|
|
return (negative ? "-" : "") + result;
|
|
}
|
|
|
|
void BigDecimal::alignScale(BigInteger& lhs, int& lhsScale,
|
|
BigInteger& rhs, int& rhsScale) {
|
|
if (lhsScale < rhsScale) {
|
|
// 左边需要扩大
|
|
for (int i = 0; i < rhsScale - lhsScale; i++) {
|
|
lhs = lhs * BigInteger(10);
|
|
}
|
|
lhsScale = rhsScale;
|
|
} else if (lhsScale > rhsScale) {
|
|
// 右边需要扩大
|
|
for (int i = 0; i < lhsScale - rhsScale; i++) {
|
|
rhs = rhs * BigInteger(10);
|
|
}
|
|
rhsScale = lhsScale;
|
|
}
|
|
}
|
|
|
|
bool BigDecimal::operator==(const BigDecimal& other) const {
|
|
if (isZero() && other.isZero()) return true;
|
|
BigInteger lhs = unscaledValue_;
|
|
BigInteger rhs = other.unscaledValue_;
|
|
int lhsScale = scale_;
|
|
int rhsScale = other.scale_;
|
|
alignScale(lhs, lhsScale, rhs, rhsScale);
|
|
return lhs == rhs;
|
|
}
|
|
|
|
bool BigDecimal::operator!=(const BigDecimal& other) const {
|
|
return !(*this == other);
|
|
}
|
|
|
|
bool BigDecimal::operator<(const BigDecimal& other) const {
|
|
if (unscaledValue_.sign() != other.unscaledValue_.sign()) {
|
|
return unscaledValue_.sign() < other.unscaledValue_.sign();
|
|
}
|
|
|
|
BigInteger lhs = unscaledValue_;
|
|
BigInteger rhs = other.unscaledValue_;
|
|
int lhsScale = scale_;
|
|
int rhsScale = other.scale_;
|
|
alignScale(lhs, lhsScale, rhs, rhsScale);
|
|
|
|
if (unscaledValue_.sign() >= 0) {
|
|
return lhs < rhs;
|
|
} else {
|
|
return lhs > rhs;
|
|
}
|
|
}
|
|
|
|
bool BigDecimal::operator>(const BigDecimal& other) const {
|
|
return other < *this;
|
|
}
|
|
|
|
bool BigDecimal::operator<=(const BigDecimal& other) const {
|
|
return *this < other || *this == other;
|
|
}
|
|
|
|
bool BigDecimal::operator>=(const BigDecimal& other) const {
|
|
return other <= *this;
|
|
}
|
|
|
|
bool BigDecimal::operator==(int value) const {
|
|
return *this == BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator!=(int value) const {
|
|
return !(*this == value);
|
|
}
|
|
|
|
bool BigDecimal::operator<(int value) const {
|
|
return *this < BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator>(int value) const {
|
|
return *this > BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator<=(int value) const {
|
|
return *this <= BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator>=(int value) const {
|
|
return *this >= BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator==(double value) const {
|
|
return *this == BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator!=(double value) const {
|
|
return !(*this == value);
|
|
}
|
|
|
|
bool BigDecimal::operator<(double value) const {
|
|
return *this < BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator>(double value) const {
|
|
return *this > BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator<=(double value) const {
|
|
return *this <= BigDecimal(value);
|
|
}
|
|
|
|
bool BigDecimal::operator>=(double value) const {
|
|
return *this >= BigDecimal(value);
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator+() const {
|
|
return *this;
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator-() const {
|
|
if (isZero()) {
|
|
return BigDecimal(0);
|
|
}
|
|
return BigDecimal(-unscaledValue_, scale_);
|
|
}
|
|
|
|
BigDecimal& BigDecimal::operator+=(const BigDecimal& other) {
|
|
*this = *this + other;
|
|
return *this;
|
|
}
|
|
|
|
BigDecimal& BigDecimal::operator-=(const BigDecimal& other) {
|
|
*this = *this - other;
|
|
return *this;
|
|
}
|
|
|
|
BigDecimal& BigDecimal::operator*=(const BigDecimal& other) {
|
|
*this = *this * other;
|
|
return *this;
|
|
}
|
|
|
|
BigDecimal& BigDecimal::operator/=(const BigDecimal& other) {
|
|
*this = *this / other;
|
|
return *this;
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator+(const BigDecimal& other) const {
|
|
if (isZero()) return other;
|
|
if (other.isZero()) return *this;
|
|
|
|
BigInteger lhs = unscaledValue_;
|
|
BigInteger rhs = other.unscaledValue_;
|
|
int lhsScale = scale_;
|
|
int rhsScale = other.scale_;
|
|
alignScale(lhs, lhsScale, rhs, rhsScale);
|
|
|
|
return BigDecimal(lhs + rhs, lhsScale);
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator-(const BigDecimal& other) const {
|
|
return *this + (-other);
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator*(const BigDecimal& other) const {
|
|
if (isZero() || other.isZero()) {
|
|
return BigDecimal(0);
|
|
}
|
|
|
|
BigInteger result = unscaledValue_ * other.unscaledValue_;
|
|
int resultScale = scale_ + other.scale_;
|
|
|
|
return BigDecimal(result, resultScale);
|
|
}
|
|
|
|
BigDecimal BigDecimal::operator/(const BigDecimal& other) const {
|
|
if (other.isZero()) {
|
|
throw std::invalid_argument("division by zero");
|
|
}
|
|
|
|
if (isZero()) {
|
|
return BigDecimal(0);
|
|
}
|
|
|
|
// 先对齐 scale,然后做整数除法
|
|
BigInteger lhs = unscaledValue_;
|
|
BigInteger rhs = other.unscaledValue_;
|
|
int lhsScale = scale_;
|
|
int rhsScale = other.scale_;
|
|
|
|
// 使用更高的精度进行除法
|
|
// 乘以 10^precision 来获得更多的精度
|
|
int precision = 10;
|
|
for (int i = 0; i < precision; i++) {
|
|
lhs = lhs * BigInteger(10);
|
|
}
|
|
|
|
BigInteger quotient = lhs / rhs;
|
|
int resultScale = lhsScale - rhsScale + precision;
|
|
|
|
return BigDecimal(quotient, resultScale);
|
|
}
|
|
|
|
BigDecimal BigDecimal::abs() const {
|
|
if (unscaledValue_.sign() >= 0) {
|
|
return *this;
|
|
}
|
|
return BigDecimal(-unscaledValue_, scale_);
|
|
}
|
|
|
|
BigDecimal BigDecimal::round(int scale) const {
|
|
if (scale < 0) {
|
|
throw std::invalid_argument("scale cannot be negative");
|
|
}
|
|
|
|
if (scale >= scale_) {
|
|
return *this;
|
|
}
|
|
|
|
// 简单实现:直接截断到指定小数位数
|
|
int diff = scale_ - scale;
|
|
BigInteger divisor = BigInteger(10);
|
|
for (int i = 1; i < diff; i++) {
|
|
divisor = divisor * BigInteger(10);
|
|
}
|
|
|
|
BigInteger rounded = unscaledValue_ / divisor;
|
|
return BigDecimal(rounded, scale);
|
|
}
|
|
|
|
} // namespace bstl
|