mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
540 lines
12 KiB
C++
540 lines
12 KiB
C++
#include "decimal.h"
|
|
|
|
decimal::decimal() {
|
|
ASSERT(_D_SIZE_>_D_PREC_);
|
|
parse_string=true;
|
|
memsetd(number, 0, _D_SIZE_);
|
|
sign=1;
|
|
trim_trailing_zeroes=true;
|
|
print_precision=DECIMAL_PRECISION;
|
|
rounding_mode=DEFAULT_ROUNDING;
|
|
parse_string=true;
|
|
}
|
|
|
|
void decimal::FromString(String s) {
|
|
int i=0, idx=-1, j=_D_SIZE_-_D_PREC_, length;
|
|
|
|
memsetd(number, 0, _D_SIZE_);
|
|
sign=1;
|
|
s=TrimLeft(TrimRight(s));
|
|
|
|
if(parse_string) {
|
|
length=s.GetCount();
|
|
if(s[i]=='-' || s[i]=='+') { length--; i++; }
|
|
while(i<s.GetCount() && IsDigit(s[i])) i++;
|
|
if(s[i]=='.') idx=i++;
|
|
while(i<s.GetCount() && IsDigit(s[i])) i++;
|
|
|
|
if(idx>=0) length--;
|
|
if(i<s.GetCount() || (length>_D_SIZE_*9)) { throw Exc("NUMBER TOO BIG TO FIT IN SPECIFIED FORMAT"); return; } // OVERFLOW
|
|
if(idx>=0 && (s.GetCount()-idx-1)>_D_PREC_*9) { throw Exc("NUMBER TOO BIG TO FIT IN SPECIFIED FORMAT"); return; } // OVERFLOW
|
|
}
|
|
if(s[0]=='-' || s[0]=='+') {
|
|
if(s[0]=='-') sign=-1;
|
|
s.Remove(0);
|
|
}
|
|
Vector<String> parts=Split(s, '.');
|
|
|
|
if(parts.GetCount()>0) {
|
|
String temp=parts[0];
|
|
length=temp.GetCount();
|
|
while(length>=9) { number[--j]=StrInt(temp.Right(9)); temp.Trim(length-=9); }
|
|
if(temp.GetCount()>0) number[--j]=StrInt(temp);
|
|
}
|
|
if(parts.GetCount()>1) {
|
|
String temp=parts[1];
|
|
j=_D_SIZE_-_D_PREC_;
|
|
length=temp.GetCount();
|
|
while(length>=9) { number[j++]=StrInt(temp.Left(9)); length-=9; temp=temp.Mid(9); }
|
|
if(temp.GetCount()>0) number[j++]=StrInt(temp+String('0', 9-temp.GetCount()));
|
|
}
|
|
j=0;
|
|
for(i=0;i<_D_SIZE_;i++) if(number[i]!=0) j++;
|
|
if(j==0) sign=1;
|
|
}
|
|
|
|
void decimal::PrintPrecision(int precision) {
|
|
print_precision=precision;
|
|
}
|
|
|
|
int decimal::PrintPrecision() {
|
|
return print_precision;
|
|
}
|
|
|
|
void decimal::RoundingMode(int rounding) {
|
|
rounding_mode=rounding;
|
|
}
|
|
|
|
int decimal::RoundingMode() {
|
|
return rounding_mode;
|
|
}
|
|
|
|
void decimal::TrimZeros(bool b) {
|
|
trim_trailing_zeroes=b;
|
|
}
|
|
|
|
bool decimal::TrimZeros() {
|
|
return trim_trailing_zeroes;
|
|
}
|
|
|
|
#define ROUND_DECIMAL(PREC, ROUND_MODE) \
|
|
switch(ROUND_MODE) { \
|
|
case ROUND_DOWN: { \
|
|
idx=s.GetCount()-_D_PREC_*9+PREC-1; \
|
|
if(s[idx+1]>'5') s.Set(idx, s[idx]+1); \
|
|
while(idx>0 && s[idx]>'9') { s.Set(idx, '0'); s.Set(--idx, s[idx]+1); } \
|
|
} \
|
|
break; \
|
|
case ROUND_UP: { \
|
|
idx=s.GetCount()-_D_PREC_*9+PREC-1; \
|
|
if(s[idx+1]>='5') s.Set(idx, s[idx]+1); \
|
|
while(idx>0 && s[idx]>'9') { s.Set(idx, '0'); s.Set(--idx, s[idx]+1); } \
|
|
} \
|
|
break; \
|
|
case ROUND_HALF_EVEN: { \
|
|
int temp=0; \
|
|
idx=s.GetCount()-_D_PREC_*9+PREC-1; \
|
|
if(s[idx+1]>'5') s.Set(idx, s[idx]+1); \
|
|
else \
|
|
if(s[idx+1]=='5') { \
|
|
if((s[idx]-'0')%2==1) s.Set(idx, s[idx]+1); \
|
|
else { \
|
|
for(int j=idx+2;j<s.GetCount();j++) temp+=(s[j]-'0'); \
|
|
if(temp>0) s.Set(idx, s[idx]+1); \
|
|
} \
|
|
} \
|
|
while(idx>0 && s[idx]>'9') { s.Set(idx, '0'); s.Set(--idx, s[idx]+1); } \
|
|
} \
|
|
break; }
|
|
|
|
decimal& decimal::Round(int precision, int rounding) {
|
|
|
|
if(IsNull(precision)) precision=print_precision;
|
|
if(IsNull(rounding)) rounding=rounding_mode;
|
|
|
|
if((precision<_D_PREC_*9)) {
|
|
String s="";
|
|
int i=0, idx;
|
|
if(sign<0) s="-";
|
|
for(;i<_D_SIZE_;i++) s+=Format("%09i", (int64)number[i]);
|
|
|
|
ROUND_DECIMAL(precision, rounding);
|
|
|
|
for(i=idx+1;i<s.GetCount();i++) s.Set(i, '0');
|
|
for(i=0;i<_D_SIZE_;i++) {
|
|
number[i]=StrInt(s.Left(9));
|
|
s=s.Mid(9);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String decimal::ToString() const {
|
|
if(isnull()) return "";
|
|
String s="";
|
|
int i=0, idx;
|
|
if(sign<0) s="-";
|
|
for(;i<_D_SIZE_;i++) s+=Format("%09i", (int64)number[i]);
|
|
if(_D_PREC_>0) {
|
|
if(s!="0") {
|
|
if((print_precision<_D_PREC_*9) || IsNull(print_precision))
|
|
ROUND_DECIMAL(print_precision, rounding_mode);
|
|
|
|
s.Insert(s.GetCount()-_D_PREC_*9, '.');
|
|
s=s.Left(s.GetCount()-_D_PREC_*9+print_precision);
|
|
if(trim_trailing_zeroes) {
|
|
int i=s.GetCount();
|
|
while(s[i-1]=='0') i--;
|
|
if(s[i-1]=='.') i--;
|
|
s.Trim(i);
|
|
}
|
|
}
|
|
else if(!trim_trailing_zeroes) s+="."+String('0', _D_PREC_*9);
|
|
}
|
|
int j=0;
|
|
if(s[0]=='-') j=1;
|
|
i=j;
|
|
while(i<s.GetCount() && s[i]=='0') i++;
|
|
if(s[i]=='.' && i>0) i--;
|
|
s.Remove(j, i-j);
|
|
if(sign<0) {
|
|
i=1;
|
|
while(i<s.GetCount() && (s[i]=='0' || s[i]=='.')) i++;
|
|
if(i>=s.GetCount()) s=s.Mid(1);
|
|
}
|
|
if(*s.Last()=='.') s=s.Left(s.GetCount()-1);
|
|
|
|
// Round();
|
|
// parse_string=false;
|
|
/* FromString(s);
|
|
parse_string=true;*/
|
|
|
|
return s;
|
|
}
|
|
|
|
decimal& decimal::operator=(decimal d) {
|
|
memcpy(number, d.number, _D_SIZE_*sizeof(int));
|
|
sign=d.sign;
|
|
|
|
return *this;
|
|
}
|
|
|
|
int decimal::CompareTo(decimal d) const {
|
|
if(sign!=d.sign)
|
|
if(sign>d.sign) return 1;
|
|
if(sign<d.sign) return -1;
|
|
|
|
int i=0;
|
|
while(i<_D_SIZE_ && number[i]==d.number[i]) i++;
|
|
if(i>=_D_SIZE_) return 0;
|
|
if(number[i]>d.number[i]) return 1;
|
|
return -1;
|
|
}
|
|
|
|
bool decimal::operator==(decimal &d) {
|
|
return (CompareTo(d)==0);
|
|
}
|
|
|
|
bool decimal::operator!=(decimal &d) {
|
|
return (CompareTo(d)!=0);
|
|
}
|
|
|
|
bool decimal::operator>=(decimal &d) {
|
|
return (CompareTo(d)>=0);
|
|
}
|
|
|
|
bool decimal::operator>(decimal &d) {
|
|
return (CompareTo(d)>0);
|
|
}
|
|
|
|
bool decimal::operator<=(decimal &d) {
|
|
return (CompareTo(d)<=0);
|
|
}
|
|
|
|
bool decimal::operator<(decimal &d) {
|
|
return (CompareTo(d)<0);
|
|
}
|
|
|
|
decimal& decimal::operator+=(decimal &d) {
|
|
if(sign==d.sign) {
|
|
decimal temp(d);
|
|
int carry=0;
|
|
|
|
for(int i=_D_SIZE_-1;i>=0;i--) {
|
|
number[i]+=(temp.number[i]+carry);
|
|
if(number[i]>999999999) { number[i]-=(int32)1000000000; carry=1; }
|
|
else carry=0;
|
|
}
|
|
if(carry>0) { throw Exc("NUMERIC OVERFLOW"); return *this; } // OVERFLOW
|
|
|
|
return *this;
|
|
}
|
|
if(sign<0) { sign=1; decimal temp(*this); *this=d; return (*this-=temp); }
|
|
decimal temp(d); temp.sign=1;
|
|
return (*this-=temp);
|
|
}
|
|
|
|
decimal decimal::operator+(decimal &d) {
|
|
decimal temp(*this);
|
|
return (temp+=d);
|
|
}
|
|
|
|
decimal decimal::operator++(int) {
|
|
decimal temp(*this);
|
|
*this+=1;
|
|
return temp;
|
|
}
|
|
|
|
decimal& decimal::operator++() {
|
|
return (*this+=1);
|
|
}
|
|
|
|
decimal& decimal::operator-=(decimal &d) {
|
|
if(sign==d.sign && sign==1) {
|
|
decimal temp(d);
|
|
if(CompareTo(d)<0) { decimal temp2(*this); *this=temp; temp=temp2; sign*=-1; }
|
|
|
|
int carry=0;
|
|
|
|
for(int i=_D_SIZE_-1;i>=0;i--) {
|
|
number[i]-=(temp.number[i]+carry);
|
|
if(number[i]<0) { number[i]+=(int32)1000000000; carry=1; }
|
|
else carry=0;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
if(sign==d.sign) { decimal temp(*this); temp.sign=1; *this=d; sign=1; return (*this-=temp); }
|
|
if(sign<0) { decimal temp(d); temp.sign=1; sign=1; *this+=temp; sign=-1; return *this; }
|
|
decimal temp(d); temp.sign=1;
|
|
return (*this+=temp);
|
|
}
|
|
|
|
decimal decimal::operator-(decimal &d) {
|
|
decimal temp(*this);
|
|
return (temp-=d);
|
|
}
|
|
|
|
decimal decimal::operator-() {
|
|
decimal temp(*this);
|
|
temp.sign*=-1;
|
|
return temp;
|
|
}
|
|
|
|
decimal decimal::operator--(int) {
|
|
decimal temp(*this);
|
|
*this-=1;
|
|
return temp;
|
|
}
|
|
|
|
decimal& decimal::operator--() {
|
|
return (*this-=1);
|
|
}
|
|
|
|
decimal& decimal::operator*=(decimal &d) {
|
|
int64 accum[(_D_SIZE_ << 1)-1], carry=0;
|
|
int64 temp, mult;
|
|
memsetd(accum, 0, ((_D_SIZE_ << 1)-1) << 1);
|
|
|
|
int idx=(_D_SIZE_ << 1)-2;
|
|
for(int i=_D_SIZE_-1;i>=0;i--) {
|
|
mult=d.number[i];
|
|
for(int j=_D_SIZE_-1,k=idx;j>=0;j--,k--) {
|
|
temp=number[j];
|
|
accum[k]=temp*mult+accum[k];
|
|
}
|
|
idx--;
|
|
}
|
|
for(int i=(_D_SIZE_ << 1)-2;i>=0;i--) {
|
|
int64 temp=accum[i]+carry;
|
|
if(temp>=1000000000) { carry=temp/1000000000; accum[i]=temp%1000000000; }
|
|
else { carry=0; accum[i]=temp; }
|
|
}
|
|
for(int i=0;i<_D_SIZE_;i++) number[i]=(int32)accum[(_D_SIZE_-1-_D_PREC_)+i];
|
|
sign*=d.sign;
|
|
if(carry>0) { throw Exc("NUMERIC OVERFLOW"); return *this; } // OVERFLOW
|
|
|
|
return *this;
|
|
}
|
|
|
|
decimal decimal::operator*(decimal &d) {
|
|
decimal temp(*this);
|
|
return (temp*=d);
|
|
}
|
|
|
|
decimal& decimal::operator/=(decimal &d) {
|
|
if(d.CompareTo(decimal(0))==0) { throw Exc("DIVISION BY ZERO!"); return *this; }
|
|
if(d.CompareTo(decimal(1))==0) return *this;
|
|
if(d.CompareTo(decimal(-1))==0) { sign*=-1; return *this; }
|
|
if(d.CompareTo(decimal(*this))==0) { return decimal::ONE; }
|
|
if(d.CompareTo(decimal(2))==0) { ShiftRight(); return *this; }
|
|
if(d.CompareTo(decimal(4))==0) { ShiftRight(); return *this; }
|
|
|
|
int _sign=sign;
|
|
sign=1;
|
|
decimal l, m(*this), v;
|
|
bool found=false, repeat=false;
|
|
int cmp=0;
|
|
|
|
if(abs(d)<ONE) {
|
|
int i=0;
|
|
String temp=d.ToString();
|
|
temp=temp.Mid(temp.Find('.')+1);
|
|
m=10;
|
|
while(temp[i++]=='0') m*=10;
|
|
}
|
|
|
|
m>>=1; l=m;
|
|
while(!found) {
|
|
v=m;
|
|
v*=d;
|
|
cmp=CompareTo(v);
|
|
repeat=(l.CompareTo(decimal::ZERO)==0);
|
|
found=(cmp==0) || repeat;
|
|
|
|
if(!found) {
|
|
l>>=1;
|
|
if(cmp<0) m-=l;
|
|
else m+=l;
|
|
}
|
|
}
|
|
|
|
_sign=_sign*d.sign;
|
|
*this=m;
|
|
sign=_sign;
|
|
|
|
return *this;
|
|
}
|
|
|
|
decimal& decimal::Sqrt() {
|
|
if(sign<0) { throw Exc("NO SQRT FOR NEGATIV NUMBERS!"); return *this; }
|
|
if(CompareTo(decimal(0))==0) { *this=ZERO; return *this; }
|
|
if(CompareTo(decimal(1))==0) return *this;
|
|
|
|
decimal l, m(*this), v;
|
|
bool found=false, repeat=false;
|
|
int cmp=0;
|
|
|
|
if(m<ONE) m=1;
|
|
m>>=1; l=m;
|
|
while(!found) {
|
|
v=m;
|
|
v*=v;
|
|
cmp=CompareTo(v);
|
|
repeat=(l.CompareTo(decimal::ZERO)==0);
|
|
found=(cmp==0) || repeat;
|
|
|
|
if(!found) {
|
|
l>>=1;
|
|
if(cmp<0) m-=l;
|
|
else m+=l;
|
|
}
|
|
}
|
|
|
|
*this=m;
|
|
|
|
return *this;
|
|
}
|
|
|
|
decimal decimal::operator/(decimal &d) {
|
|
decimal temp(*this);
|
|
return (temp/=d);
|
|
}
|
|
|
|
decimal& decimal::ShiftLeft() {
|
|
int carry=0;
|
|
for(int i=_D_SIZE_-1;i>=0;i--) {
|
|
number[i]<<=1;
|
|
number[i]+=carry;
|
|
carry=(number[i]>=1000000000)?1:0;
|
|
if(carry) number[i]%=1000000000;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
decimal& decimal::ShiftRight() {
|
|
int carry=0, temp=0;
|
|
for(int i=0;i<_D_SIZE_;i++) {
|
|
temp=number[i]&1;
|
|
number[i]>>=1;
|
|
number[i]+=carry;
|
|
carry=(temp)?500000000:0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
decimal& decimal::operator<<=(int n) {
|
|
for(int i=0;i<n;i++) ShiftLeft();
|
|
return *this;
|
|
}
|
|
|
|
decimal decimal::operator<<(int n) {
|
|
decimal temp(*this);
|
|
return (temp<<=n);
|
|
}
|
|
|
|
decimal& decimal::operator>>=(int n) {
|
|
for(int i=0;i<n;i++) ShiftRight();
|
|
return *this;
|
|
}
|
|
|
|
decimal decimal::operator>>(int n) {
|
|
decimal temp(*this);
|
|
return (temp>>=n);
|
|
}
|
|
|
|
decimal& decimal::operator^=(int n) {
|
|
decimal temp(*this);
|
|
for(int i=1;i<n;i++) *this*=temp;
|
|
return *this;
|
|
}
|
|
|
|
decimal decimal::operator^(int n) {
|
|
decimal temp(*this);
|
|
return (temp^=n);
|
|
}
|
|
|
|
bool decimal::isnull() const {
|
|
return (number[0]==1111111111);
|
|
}
|
|
|
|
decimal& decimal::Absolute() {
|
|
sign=1;
|
|
return *this;
|
|
}
|
|
|
|
decimal& decimal::Floor() {
|
|
int32 temp=0;
|
|
for(int i=_D_SIZE_-_D_PREC_;i<_D_SIZE_;i++) { temp|=number[i]; number[i]=0; }
|
|
if(sign>=0) return *this;
|
|
if(temp>0) --*this;
|
|
return *this;
|
|
}
|
|
|
|
decimal& decimal::Ceil() {
|
|
int32 temp=0;
|
|
for(int i=_D_SIZE_-_D_PREC_;i<_D_SIZE_;i++) { temp|=number[i]; number[i]=0; }
|
|
if(sign<0) return *this;
|
|
if(temp>0) ++*this;
|
|
return *this;
|
|
}
|
|
|
|
bool decimal::IsNullInstance() const { return isnull(); }
|
|
|
|
decimal::operator Value() const { return RichToValue(*this); }
|
|
|
|
decimal::decimal(const Value& v, int precision, int rounding) { FromString(AsString(v)); trim_trailing_zeroes=true; print_precision=precision; rounding_mode=rounding; }
|
|
|
|
int decimal::Compare(const Value& b) const { return CompareTo(b); }
|
|
|
|
void decimal::Serialize(Stream &s) { s.Put(ToString()); }
|
|
|
|
unsigned decimal::GetHashValue() const {
|
|
return ::GetHashValue(ToString());;
|
|
}
|
|
|
|
decimal abs(decimal d) {
|
|
return d.Absolute();
|
|
}
|
|
|
|
decimal floor(decimal d) {
|
|
return d.Floor();
|
|
}
|
|
|
|
decimal ceil(decimal d) {
|
|
return d.Ceil();
|
|
}
|
|
|
|
decimal sqrt(decimal d) {
|
|
return d.Sqrt();
|
|
}
|
|
|
|
decimal round(decimal d, int precision, int rounding) {
|
|
decimal temp(d);
|
|
return temp.Round(precision, rounding);
|
|
}
|
|
|
|
static decimal operator+(Value t, decimal d) {
|
|
decimal temp(t);
|
|
return (temp+=d);
|
|
}
|
|
|
|
static decimal operator-(Value t, decimal d) {
|
|
decimal temp(t);
|
|
return (temp-=d);
|
|
}
|
|
|
|
static decimal operator*(Value t, decimal d) {
|
|
decimal temp(t);
|
|
return (temp*=d);
|
|
}
|
|
|
|
static decimal operator/(Value t, decimal d) {
|
|
decimal temp(t);
|
|
return (temp/=d);
|
|
}
|
|
|
|
decimal decimal::ZERO=decimal(0);
|
|
decimal decimal::ONE=decimal(1);
|
|
decimal decimal::TWO=decimal(2);
|
|
decimal decimal::TEN=decimal(10);
|