public BigDecimal add(BigDecimal augend, MathContext mc) { BigDecimal larger; // operand with the largest unscaled value BigDecimal smaller; // operand with the smallest unscaled value BigInteger tempBI; long diffScale = (long)this._scale - augend._scale; int largerSignum; // Some operand is zero or the precision is infinity if ((augend.isZero()) || (this.isZero()) || (mc.getPrecision() == 0)) { return add(augend).round(mc); } // Cases where there is room for optimizations if (this.aproxPrecision() < diffScale - 1) { larger = augend; smaller = this; } else if (augend.aproxPrecision() < -diffScale - 1) { larger = this; smaller = augend; } else {// No optimization is done return add(augend).round(mc); } if (mc.getPrecision() >= larger.aproxPrecision()) { // No optimization is done return add(augend).round(mc); } // Cases where it's unnecessary to add two numbers with very different scales largerSignum = larger.signum(); if (largerSignum == smaller.signum()) { tempBI = Multiplication.multiplyByPositiveInt(larger.getUnscaledValue(),10) .add(BigInteger.valueOf(largerSignum)); } else { tempBI = larger.getUnscaledValue().subtract( BigInteger.valueOf(largerSignum)); tempBI = Multiplication.multiplyByPositiveInt(tempBI,10) .add(BigInteger.valueOf(largerSignum * 9)); } // Rounding the improved adding larger = new BigDecimal(tempBI, larger._scale + 1); return larger.round(mc); }
private BigDecimal dodivide(char code,BigDecimal rhs,MathContext set,int scale) { BigDecimal lhs; int reqdig; int newexp; BigDecimal res; int newlen; sbyte[] var1; int var1len; sbyte[] var2; int var2len; int b2b; int have; int thisdigit=0; int i=0; int ix=0; sbyte v2=0; int ba=0; int mult=0; int start=0; int padding=0; int d=0; sbyte[] newvar1=null; sbyte lasthave=0; int actdig=0; sbyte[] newmant=null; if (set.lostDigits) checkdigits(rhs,set.digits); lhs=this; // name for clarity // [note we must have checked lostDigits before the following checks] if (rhs.ind==0) throw new System.ArithmeticException("Divide by 0"); // includes 0/0 if (lhs.ind==0) { // 0/x => 0 [possibly with .0s] if (set.form!=MathContext.PLAIN) return ZERO; if (scale==(-1)) return lhs; return lhs.setScale(scale); } /* Prepare numbers according to BigDecimal rules */ reqdig=set.digits; // local copy (heavily used) if (reqdig>0) { if (lhs.mant.Length>reqdig) lhs=clone(lhs).round(set); if (rhs.mant.Length>reqdig) rhs=clone(rhs).round(set); } else {/* scaled divide */ if (scale==(-1)) scale=lhs.scale(); // set reqdig to be at least large enough for the computation reqdig=lhs.mant.Length; // base length // next line handles both positive lhs.exp and also scale mismatch if (scale!=((int)-lhs.exp)) reqdig=(reqdig+scale)+lhs.exp; reqdig=(reqdig-((rhs.mant.Length-1)))-rhs.exp; // reduce by RHS effect if (reqdig<lhs.mant.Length) reqdig=lhs.mant.Length; // clamp if (reqdig<rhs.mant.Length) reqdig=rhs.mant.Length; // .. } /* precalculate exponent */ newexp=((lhs.exp-rhs.exp)+lhs.mant.Length)-rhs.mant.Length; /* If new exponent -ve, then some quick exits are possible */ if (newexp<0) if (code!='D') { if (code=='I') return ZERO; // easy - no integer part /* Must be 'R'; remainder is [finished clone of] input value */ return clone(lhs).finish(set,false); } /* We need slow division */ res=new BigDecimal(); // where we'll build result res.ind=(sbyte)(lhs.ind*rhs.ind); // final sign (for D/I) res.exp=newexp; // initial exponent (for D/I) res.mant=new sbyte[reqdig+1]; // where build the result /* Now [virtually pad the mantissae with trailing zeros */ // Also copy the LHS, which will be our working array newlen=(reqdig+reqdig)+1; var1=extend(lhs.mant,newlen); // always makes longer, so new safe array var1len=newlen; // [remaining digits are 0] var2=rhs.mant; var2len=newlen; /* Calculate first two digits of rhs (var2), +1 for later estimations */ b2b=(var2[0]*10)+1; if (var2.Length>1) b2b=b2b+var2[1]; /* start the long-division loops */ have=0; {for(;;){ thisdigit=0; /* find the next digit */ {inner:for(;;){ if (var1len<var2len) break; // V1 too low if (var1len==var2len) { // compare needed for(ix=var1len,i=0;ix>0;ix--,i++){ // var1len is always <= var1.Length if (i<var2.Length) v2=var2[i]; else v2=(sbyte)0; if (var1[i]<v2) goto breakInner; // V1 too low if (var1[i]>v2) goto breakCompare; // OK to subtract } /* reach here if lhs and rhs are identical; subtraction will increase digit by one, and the residue will be 0 so we are done; leave the loop with residue set to 0 (in case code is 'R' or ROUND_UNNECESSARY or a ROUND_HALF_xxxx is being checked) */ thisdigit++; res.mant[have]=(sbyte)thisdigit; have++; var1[0]=(sbyte)0; // residue to 0 [this is all we'll test] // var1len=1 -- [optimized out] goto breakOuter; breakCompare: /* prepare for subtraction. Estimate BA (lengths the same) */ ba=(int)var1[0]; // use only first digit } // lengths the same else {/* lhs longer than rhs */ /* use first two digits for estimate */ ba=var1[0]*10; if (var1len>1) ba=ba+var1[1]; } /* subtraction needed; V1>=V2 */ mult=(ba*10)/b2b; if (mult==0) mult=1; thisdigit=thisdigit+mult; // subtract; var1 reusable var1=byteaddsub(var1,var1len,var2,var2len,(int)-mult,true); if (var1[0]!=0) goto inner; // maybe another subtract needed /* V1 now probably has leading zeros, remove leading 0's and try again. (It could be longer than V2) */ for(ix=var1len-2,start=0;start<=ix;start++){ if (var1[start]!=0) break; var1len--; } if (start==0) goto inner; // shift left System.Array.Copy(var1,start,var1,0,var1len); } }/*inner*/ breakInner: /* We have the next digit */ if ((have!=0)|(thisdigit!=0)) { // put the digit we got res.mant[have]=(sbyte)thisdigit; have++; if (have==(reqdig+1)) goto breakOuter; // we have all we need if (var1[0]==0) goto breakOuter; // residue now 0 } /* can leave now if a scaled divide and exponent is small enough */ if (scale>=0) if (((int)-res.exp)>scale) goto breakOuter; /* can leave now if not Divide and no integer part left */ if (code!='D') if (res.exp<=0) goto breakOuter; res.exp=res.exp-1; // reduce the exponent /* to get here, V1 is less than V2, so divide V2 by 10 and go for the next digit */ var2len--; } }/*outer*/ breakOuter: /* here when we have finished dividing, for some reason */ // have is the number of digits we collected in res.mant if (have==0) have=1; // res.mant[0] is 0; we always want a digit if ((code=='I')|(code=='R')) {/* check for integer overflow needed */ if ((have+res.exp)>reqdig) throw new System.ArithmeticException("Integer overflow"); if (code=='R') { /* We were doing Remainder -- return the residue */ if (res.mant[0]==0) // no integer part was found return clone(lhs).finish(set,false); // .. so return lhs, canonical if (var1[0]==0) return ZERO; // simple 0 residue res.ind=lhs.ind; // sign is always as LHS /* Calculate the exponent by subtracting the number of padding zeros we added and adding the original exponent */ padding=((reqdig+reqdig)+1)-lhs.mant.Length; res.exp=(res.exp-padding)+lhs.exp; /* strip insignificant padding zeros from residue, and create/copy the resulting mantissa if need be */ d=var1len; for(i=d-1;i>=1;i--){if(!((res.exp<lhs.exp)&(res.exp<rhs.exp)))break; if (var1[i]!=0) break; d--; res.exp=res.exp+1; } if (d<var1.Length) {/* need to reduce */ newvar1=new sbyte[d]; System.Array.Copy(var1,0,newvar1,0,d); // shorten var1=newvar1; } res.mant=var1; return res.finish(set,false); } } else {/* 'D' -- no overflow check needed */ // If there was a residue then bump the final digit (iff 0 or 5) // so that the residue is visible for ROUND_UP, ROUND_HALF_xxx and // ROUND_UNNECESSARY checks (etc.) later. // [if we finished early, the residue will be 0] if (var1[0]!=0) { // residue not 0 lasthave=res.mant[have-1]; if (((lasthave%5))==0) res.mant[have-1]=(sbyte)(lasthave+1); } } /* Here for Divide or Integer Divide */ // handle scaled results first ['I' always scale 0, optional for 'D'] if (scale>=0) { // say 'scale have res.exp len' scale have res.exp res.mant.Length if (have!=res.mant.Length) // already padded with 0's, so just adjust exponent res.exp=res.exp-((res.mant.Length-have)); // calculate number of digits we really want [may be 0] actdig=res.mant.Length-((((int)-res.exp)-scale)); res.round(actdig,set.roundingMode); // round to desired length // This could have shifted left if round (say) 0.9->1[.0] // Repair if so by adding a zero and reducing exponent if (res.exp!=((int)-scale)) { res.mant=extend(res.mant,res.mant.Length+1); res.exp=res.exp-1; } return res.finish(set,true); // [strip if not PLAIN] } // reach here only if a non-scaled if (have==res.mant.Length) { // got digits+1 digits res.round(set); have=reqdig; } else {/* have<=reqdig */ if (res.mant[0]==0) return ZERO; // fastpath // make the mantissa truly just 'have' long // [we could let finish do this, during strip, if we adjusted // the exponent; however, truncation avoids the strip loop] newmant=new sbyte[have]; // shorten System.Array.Copy(res.mant,0,newmant,0,have); res.mant=newmant; } return res.finish(set,true); }
public BigDecimal subtract(BigDecimal subtrahend, MathContext mc) { long diffScale = subtrahend._scale - (long)this._scale; int thisSignum; BigDecimal leftOperand; // it will be only the left operand (this) BigInteger tempBI; // Some operand is zero or the precision is infinity if ((subtrahend.isZero()) || (this.isZero()) || (mc.getPrecision() == 0)) { return subtract(subtrahend).round(mc); } // Now: this != 0 and subtrahend != 0 if (subtrahend.aproxPrecision() < diffScale - 1) { // Cases where it is unnecessary to subtract two numbers with very different scales if (mc.getPrecision() < this.aproxPrecision()) { thisSignum = this.signum(); if (thisSignum != subtrahend.signum()) { tempBI = Multiplication.multiplyByPositiveInt(this.getUnscaledValue(), 10) .add(BigInteger.valueOf(thisSignum)); } else { tempBI = this.getUnscaledValue().subtract(BigInteger.valueOf(thisSignum)); tempBI = Multiplication.multiplyByPositiveInt(tempBI, 10) .add(BigInteger.valueOf(thisSignum * 9)); } // Rounding the improved subtracting leftOperand = new BigDecimal(tempBI, this._scale + 1); return leftOperand.round(mc); } } // No optimization is done return subtract(subtrahend).round(mc); }