ultimatepp/uppdev/Decimal/decimal.cpp
cxl 4a1c627474 Adding uppdev....
git-svn-id: svn://ultimatepp.org/upp/trunk@328 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-08-15 08:36:24 +00:00

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);