bigmoney.js
Version:
Library for handling the money values. Based on big.js library for arbitrary-precision decimal arithmetic. Support multiple currencies.
89 lines • 12.4 kB
JavaScript
;(function(global){'use strict';Big['DP']=20;Big['RM']=1;var MAX_DP=1E6,MAX_POWER=1E6,TO_EXP_NEG=-7,TO_EXP_POS=21,P=Big.prototype,isValid=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,ONE=new Big(1);function Big(n){var i,j,nL,x=this;if(!(x instanceof Big)){return new Big(n)}
if(n instanceof Big){x['s']=n['s'];x['e']=n['e'];x['c']=n['c'].slice();return}
if(n===0&&1 / n<0){n='-0'}else if(!isValid.test(n+='')){throwErr(NaN)}
x['s']=n.charAt(0)=='-'?(n=n.slice(1),-1):1;if((i=n.indexOf('.'))>-1){n=n.replace('.','')}
if((j=n.search(/e/i))>0){if(i<0){i=j}
i+=+n.slice(j+1);n=n.substring(0,j)}else if(i<0){i=n.length}
for(j=0;n.charAt(j)=='0';j++){}
if(j==(nL=n.length)){x['c']=[x['e']=0]}else{for(;n.charAt(--nL)=='0';){}
x['e']=i-j-1;x['c']=[];for(i=0;j<=nL;x['c'][i++]=+n.charAt(j++)){}}}
function rnd(x,dp,rm,more){var xc=x['c'],i=x['e']+dp+1;if(rm===1){more=xc[i]>=5}else if(rm===2){more=xc[i]>5||xc[i]==5&&(more||i<0||xc[i+1]!=null||xc[i-1]&1)}else if(rm===3){more=more||xc[i]!=null||i<0}else if(more=false,rm!==0){throwErr('!Big.RM!')}
if(i<1||!xc[0]){x['c']=more?(x['e']=-dp,[1]):[x['e']=0];}else{xc.length=i--;if(more){for(;++xc[i]>9;){xc[i]=0;if(!i--){++x['e'];xc.unshift(1)}}}
for(i=xc.length;!xc[--i];xc.pop()){}}
return x}
function throwErr(message){var err=new Error(message);err['name']='BigError';throw err}
P['abs']=function(){var x=new Big(this);x['s']=1;return x};P['cmp']=function(y){var xNeg,x=this,xc=x['c'],yc=(y=new Big(y))['c'],i=x['s'],j=y['s'],k=x['e'],l=y['e'];if(!xc[0]||!yc[0]){return!xc[0]?!yc[0]?0:-j:i}
if(i!=j){return i}
xNeg=i<0;if(k!=l){return k>l^xNeg?1:-1}
for(i=-1,j=(k=xc.length)<(l=yc.length)?k:l;++i<j;){if(xc[i]!=yc[i]){return xc[i]>yc[i]^xNeg?1:-1}}
return k==l?0:k>l^xNeg?1:-1};P['div']=function(y){var x=this,dvd=x['c'],dvs=(y=new Big(y))['c'],s=x['s']==y['s']?1:-1,dp=Big['DP'];if(dp!==~~dp||dp<0||dp>MAX_DP){throwErr('!Big.DP!')}
if(!dvd[0]||!dvs[0]){if(dvd[0]==dvs[0]){throwErr(NaN)}
if(!dvs[0]){throwErr(s / 0)}
return new Big(s*0)}
var dvsL,dvsT,next,cmp,remI,dvsZ=dvs.slice(),dvdI=dvsL=dvs.length,dvdL=dvd.length,rem=dvd.slice(0,dvsL),remL=rem.length,quo=new Big(ONE),qc=quo['c']=[],qi=0,digits=dp+(quo['e']=x['e']-y['e'])+1;quo['s']=s;s=digits<0?0:digits;dvsZ.unshift(0);for(;remL++<dvsL;rem.push(0)){}
do{for(next=0;next<10;next++){if(dvsL!=(remL=rem.length)){cmp=dvsL>remL?1:-1}else{for(remI=-1,cmp=0;++remI<dvsL;){if(dvs[remI]!=rem[remI]){cmp=dvs[remI]>rem[remI]?1:-1;break}}}
if(cmp<0){for(dvsT=remL==dvsL?dvs:dvsZ;remL;){if(rem[--remL]<dvsT[remL]){for(remI=remL;remI&&!rem[--remI];rem[remI]=9){}
--rem[remI];rem[remL]+=10}
rem[remL]-=dvsT[remL]}
for(;!rem[0];rem.shift()){}}else{break}}
qc[qi++]=cmp?next:++next;rem[0]&&cmp?(rem[remL]=dvd[dvdI]||0):(rem=[dvd[dvdI]])}while((dvdI++<dvdL||rem[0]!=null)&&s--);if(!qc[0]&&qi!=1){qc.shift();quo['e']--;}
if(qi>digits){rnd(quo,dp,Big['RM'],rem[0]!=null)}
return quo}
P['eq']=function(y){return!this.cmp(y)};P['gt']=function(y){return this.cmp(y)>0};P['gte']=function(y){return this.cmp(y)>-1};P['lt']=function(y){return this.cmp(y)<0};P['lte']=function(y){return this.cmp(y)<1};P['minus']=function(y){var d,i,j,xLTy,x=this,a=x['s'],b=(y=new Big(y))['s'];if(a!=b){return y['s']=-b,x['plus'](y)}
var xc=x['c'].slice(),xe=x['e'],yc=y['c'],ye=y['e'];if(!xc[0]||!yc[0]){return yc[0]?(y['s']=-b,y):new Big(xc[0]?x:0)}
if(a=xe-ye){d=(xLTy=a<0)?(a=-a,xc):(ye=xe,yc);for(d.reverse(),b=a;b--;d.push(0)){}
d.reverse()}else{j=((xLTy=xc.length<yc.length)?xc:yc).length;for(a=b=0;b<j;b++){if(xc[b]!=yc[b]){xLTy=xc[b]<yc[b];break}}}
if(xLTy){d=xc,xc=yc,yc=d;y['s']=-y['s']}
if((b=-((j=xc.length)-yc.length))>0){for(;b--;xc[j++]=0){}}
for(b=yc.length;b>a;){if(xc[--b]<yc[b]){for(i=b;i&&!xc[--i];xc[i]=9){}
--xc[i];xc[b]+=10}
xc[b]-=yc[b]}
for(;xc[--j]==0;xc.pop()){}
for(;xc[0]==0;xc.shift(),--ye){}
if(!xc[0]){y['s']=1;xc=[ye=0]}
return y['c']=xc,y['e']=ye,y};P['mod']=function(y){y=new Big(y);var c,x=this,i=x['s'],j=y['s'];if(!y['c'][0]){throwErr(NaN)}
x['s']=y['s']=1;c=y.cmp(x)==1;x['s']=i,y['s']=j;return c?new Big(x):(i=Big['DP'],j=Big['RM'],Big['DP']=Big['RM']=0,x=x['div'](y),Big['DP']=i,Big['RM']=j,this['minus'](x['times'](y)))};P['plus']=function(y){var d,x=this,a=x['s'],b=(y=new Big(y))['s'];if(a!=b){return y['s']=-b,x['minus'](y)}
var xe=x['e'],xc=x['c'],ye=y['e'],yc=y['c'];if(!xc[0]||!yc[0]){return yc[0]?y:new Big(xc[0]?x:a*0)}
if(xc=xc.slice(),a=xe-ye){d=a>0?(ye=xe,yc):(a=-a,xc);for(d.reverse();a--;d.push(0)){}
d.reverse()}
if(xc.length-yc.length<0){d=yc,yc=xc,xc=d}
for(a=yc.length,b=0;a;b=(xc[--a]=xc[a]+yc[a]+b)/ 10^0,xc[a]%=10){}
if(b){xc.unshift(b);++ye}
for(a=xc.length;xc[--a]==0;xc.pop()){}
return y['c']=xc,y['e']=ye,y};P['pow']=function(e){var isNeg=e<0,x=new Big(this),y=ONE;if(e!==~~e||e<-MAX_POWER||e>MAX_POWER){throwErr('!pow!')}
for(e=isNeg?-e:e;;){if(e&1){y=y['times'](x)}
e>>=1;if(!e){break}
x=x['times'](x)}
return isNeg?ONE['div'](y):y};P['round']=function(dp,rm){var x=new Big(this);if(dp==null){dp=0}else if(dp!==~~dp||dp<0||dp>MAX_DP){throwErr('!round!')}
rnd(x,dp,rm==null?Big['RM']:rm);return x};P['sqrt']=function(){var estimate,r,approx,x=this,xc=x['c'],i=x['s'],e=x['e'],half=new Big('0.5');if(!xc[0]){return new Big(x)}
if(i<0){throwErr(NaN)}
i=Math.sqrt(x.toString());if(i==0||i==1 / 0){estimate=xc.join('');if(!(estimate.length+e&1)){estimate+='0'}
r=new Big(Math.sqrt(estimate).toString());r['e']=(((e+1)/ 2)|0)-(e<0||e&1)}else{r=new Big(i.toString())}
i=r['e']+(Big['DP']+=4);do{approx=r;r=half['times'](approx['plus'](x['div'](approx)))}while(approx['c'].slice(0,i).join('')!==r['c'].slice(0,i).join(''));rnd(r,Big['DP']-=4,Big['RM']);return r};P['times']=function(y){var c,x=this,xc=x['c'],yc=(y=new Big(y))['c'],a=xc.length,b=yc.length,i=x['e'],j=y['e'];y['s']=x['s']==y['s']?1:-1;if(!xc[0]||!yc[0]){return new Big(y['s']*0)}
y['e']=i+j;if(a<b){c=xc,xc=yc,yc=c,j=a,a=b,b=j}
for(j=a+b,c=[];j--;c.push(0)){}
for(i=b-1;i>-1;i--){for(b=0,j=a+i;j>i;b=c[j]+yc[i]*xc[j-i-1]+b,c[j--]=b%10|0,b=b / 10|0){}
if(b){c[j]=(c[j]+b)%10}}
b&&++y['e'];!c[0]&&c.shift();for(j=c.length;!c[--j];c.pop()){}
return y['c']=c,y};P['toString']=P['valueOf']=P['toJSON']=function(){var x=this,e=x['e'],str=x['c'].join(''),strL=str.length;if(e<=TO_EXP_NEG||e>=TO_EXP_POS){str=str.charAt(0)+(strL>1?'.'+str.slice(1):'')+
(e<0?'e':'e+')+e}else if(e<0){for(;++e;str='0'+str){}
str='0.'+str}else if(e>0){if(++e>strL){for(e-=strL;e--;str+='0'){}}else if(e<strL){str=str.slice(0,e)+'.'+str.slice(e)}}else if(strL>1){str=str.charAt(0)+'.'+str.slice(1)}
return x['s']<0&&x['c'][0]?'-'+str:str};function format(x,dp,toE){var i=dp-(x=new Big(x))['e'],c=x['c'];if(c.length>++dp){rnd(x,i,Big['RM'])}
i=!c[0]?i+1:toE?dp:(c=x['c'],x['e']+i+1);for(;c.length<i;c.push(0)){}
i=x['e'];return toE==1||toE==2&&(dp<=i||i<=TO_EXP_NEG)?(x['s']<0&&c[0]?'-':'')+(c.length>1?(c.splice(1,0,'.'),c.join('')):c[0])+(i<0?'e':'e+')+i:x.toString()}
P['toExponential']=function(dp){if(dp==null){dp=this['c'].length-1}else if(dp!==~~dp||dp<0||dp>MAX_DP){throwErr('!toExp!')}
return format(this,dp,1)};P['toFixed']=function(dp){var str,x=this,neg=TO_EXP_NEG,pos=TO_EXP_POS;TO_EXP_NEG=-(TO_EXP_POS=1 / 0);if(dp==null){str=x.toString()}else if(dp===~~dp&&dp>=0&&dp<=MAX_DP){str=format(x,x['e']+dp);if(x['s']<0&&x['c'][0]&&str.indexOf('-')<0){str='-'+str}}
TO_EXP_NEG=neg,TO_EXP_POS=pos;if(!str){throwErr('!toFix!')}
return str};P['toPrecision']=function(sd){if(sd==null){return this.toString()}else if(sd!==~~sd||sd<1||sd>MAX_DP){throwErr('!toPre!')}
return format(this,sd-1,2)};if(typeof module!=='undefined'&&module.exports){module.exports=Big}else if(typeof define=='function'&&define.amd){define(function(){return Big})}else{global['Big']=Big}})(this);;(function(root,undefined){var Big=root.Big||require('big.js');var settings={base:"USD",rates:{},format:"%decimal %currency"};function Money(val,currency,options){if(!(this instanceof Money)){return new Money(val,currency,options);}
if(arguments.length===2&&typeof currency==='object'){options=currency;currency=undefined;}
this.options=options||{};this.val=Big(val);this.currency=currency||this.options.currency||Money.settings.base;}
Money.prototype.abs=function(){return Money(this.val.abs.apply(this.val,arguments),this.currency,this.options);};Money.prototype.cmp=function(){return this.val.cmp.apply(this.val,arguments);};Money.prototype.div=function(){return Money(this.val.div.apply(this.val,arguments),this.currency,this.options);};Money.prototype.eq=function(){return this.val.eq.apply(this.val,arguments);};Money.prototype.gt=function(){return this.val.gt.apply(this.val,arguments);};Money.prototype.gte=function(){return this.val.gte.apply(this.val,arguments);};Money.prototype.lt=function(){return this.val.lt.apply(this.val,arguments);};Money.prototype.lte=function(){return this.val.lte.apply(this.val,arguments);};Money.prototype.minus=function(){return Money(this.val.minus.apply(this.val,arguments),this.currency,this.options);};Money.prototype.mod=function(){return Money(this.val.mod.apply(this.val,arguments),this.currency,this.options);};Money.prototype.plus=function(){return Money(this.val.plus.apply(this.val,arguments),this.currency,this.options);};Money.prototype.pow=function(){return this.val.pow.apply(this.val,arguments);};Money.prototype.round=function(){return Money(this.val.round.apply(this.val,arguments),this.currency,this.options);};Money.prototype.sqrt=function(){return Money(this.val.sqrt.apply(this.val,arguments),this.currency,this.options);};Money.prototype.times=function(){return Money(this.val.times.apply(this.val,arguments),this.currency,this.options);};function sum(a,b){return a+b;}
function ones(len){var arr=[];for(var i=0;i<len;i++){arr.push(1);}
return arr;}
Money.prototype.allocate=function(ratios){if(typeof ratios==='undefined'){return[this];}else if(typeof ratios==='number'){ratios=ones(ratios);}
var amount=this,remainder=amount,total=ratios.reduce(sum),results=[],current=0;ratios.forEach(function(ratio,index){results.push(amount.times(ratio).div(total));remainder=remainder.minus(results[index]);});while(!remainder.eq(0)){results[current]=results[current++].plus(0.01*remainder.val.s);if(current>=results.length){current=0;}
remainder=remainder.plus(0.01*remainder.val.s*-1);}
return results;};Money.prototype.convert=function(to){to||(to=Money.settings.base);var rate,val,from=this.currency;if(from===to)return Money(this.val,to,this.options);if(from===Money.settings.base){rate=Money.settings.rates[to];if(rate===undefined)throw new Error('Unknown rate for "'+to+'" currency');val=this.val.times(rate);return Money(val,to,this.options);}
else if(to===Money.settings.base){rate=Money.settings.rates[from];if(rate===undefined)throw new Error('Unknown rate for "'+from+'" currency');val=rate===0?0:this.val.div(rate);return Money(val,to,this.options);}
else{return(this.convert(Money.settings.base)).convert(to);}};Money.prototype.valueOf=function(){return parseFloat(this.val.toFixed(2));};Money.prototype.toString=function(){return this.val.toFixed(2);};Money.prototype.format=function(formatTemplate){return Money.formatter(this.valueOf(),this.currency,formatTemplate);};Money.isValidCurrency=function(curr){return typeof Money.settings.rates[curr]==='number';};Money.settings=settings;Money.formatter=function(decimal,currency,formatTemplate){formatTemplate||(formatTemplate=Money.settings.format);return formatTemplate.replace('%decimal',decimal).replace('%currency',currency);};if(typeof module!=='undefined'&&module.exports){module.exports=Money;}else if(typeof define=='function'&&define.amd){define(function(){return Money});}else{Money.noConflict=(function(oldMoney){return function(){root.Money=oldMoney;Money.noConflict=undefined;return Money;};})(root.Money);root['Money']=Money;}})(this);