Beispiel #1
0
 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);
 }
Beispiel #2
0
        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);
        }
Beispiel #3
0
 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);
 }