Beispiel #1
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 #2
0
        public BigDecimal add(BigDecimal rhs,MathContext set)
        {
            BigDecimal lhs;
            int reqdig;
            BigDecimal res;
            sbyte[] usel;
            int usellen;
            sbyte[] user;
            int userlen;
            int newlen=0;
            int tlen=0;
            int mult=0;
            sbyte[] t=null;
            int ia=0;
            int ib=0;
            int ea=0;
            int eb=0;
            sbyte ca=0;
            sbyte cb=0;
            /* determine requested digits and form */
            if (set.lostDigits)
                checkdigits(rhs,set.digits);
            lhs=this; // name for clarity and proxy

            /* Quick exit for add floating 0 */
            // plus() will optimize to return same object if possible
            if (lhs.ind==0)
                if (set.form!=MathContext.PLAIN)
                    return rhs.plus(set);
            if (rhs.ind==0)
                if (set.form!=MathContext.PLAIN)
                    return lhs.plus(set);

            /* Prepare numbers (round, unless unlimited precision) */
            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);
                    // [we could reuse the new LHS for result in this case]
                }

            res=new BigDecimal(); // build result here

            /* Now see how much we have to pad or truncate lhs or rhs in order
               to align the numbers.  If one number is much larger than the
               other, then the smaller cannot affect the answer [but we may
               still need to pad with up to DIGITS trailing zeros]. */
            // Note sign may be 0 if digits (reqdig) is 0
            // usel and user will be the sbyte arrays passed to the adder; we'll
            // use them on all paths except quick exits
            usel=lhs.mant;
            usellen=lhs.mant.Length;
            user=rhs.mant;
            userlen=rhs.mant.Length;
                    if (lhs.exp==rhs.exp)
                        {/* no padding needed */
                            // This is the most common, and fastest, path
                            res.exp=lhs.exp;
                        }
                    else if (lhs.exp>rhs.exp)
                        { // need to pad lhs and/or truncate rhs
                            newlen=(usellen+lhs.exp)-rhs.exp;
                            /* If, after pad, lhs would be longer than rhs by digits+1 or
                               more (and digits>0) then rhs cannot affect answer, so we only
                               need to pad up to a length of DIGITS+1. */
                            if (newlen>=((userlen+reqdig)+1))
                                if (reqdig>0)
                                    {
                                        // LHS is sufficient
                                        res.mant=usel;
                                        res.exp=lhs.exp;
                                        res.ind=lhs.ind;
                                        if (usellen<reqdig)
                                            { // need 0 padding
                                                res.mant=extend(lhs.mant,reqdig);
                                                res.exp=res.exp-((reqdig-usellen));
                                            }
                                        return res.finish(set,false);
                                    }
                            // RHS may affect result
                            res.exp=rhs.exp; // expected final exponent
                            if (newlen>(reqdig+1))
                                if (reqdig>0)
                                    {
                                        // LHS will be max; RHS truncated
                                        tlen=(newlen-reqdig)-1; // truncation length
                                        userlen=userlen-tlen;
                                        res.exp=res.exp+tlen;
                                        newlen=reqdig+1;
                                    }
                            if (newlen>usellen)
                                usellen=newlen; // need to pad LHS
                        }
                    else{ // need to pad rhs and/or truncate lhs
                        newlen=(userlen+rhs.exp)-lhs.exp;
                        if (newlen>=((usellen+reqdig)+1))
                            if (reqdig>0)
                                {
                                    // RHS is sufficient
                                    res.mant=user;
                                    res.exp=rhs.exp;
                                    res.ind=rhs.ind;
                                    if (userlen<reqdig)
                                        { // need 0 padding
                                            res.mant=extend(rhs.mant,reqdig);
                                            res.exp=res.exp-((reqdig-userlen));
                                        }
                                    return res.finish(set,false);
                                }
                        // LHS may affect result
                        res.exp=lhs.exp; // expected final exponent
                        if (newlen>(reqdig+1))
                            if (reqdig>0)
                                {
                                    // RHS will be max; LHS truncated
                                    tlen=(newlen-reqdig)-1; // truncation length
                                    usellen=usellen-tlen;
                                    res.exp=res.exp+tlen;
                                    newlen=reqdig+1;
                                }
                        if (newlen>userlen)
                            userlen=newlen; // need to pad RHS
                    }

            if (lhs.ind==iszero)
                res.ind=ispos;
            else
                res.ind=lhs.ind; // likely sign, all paths
            if (((lhs.ind==isneg)?1:0)==((rhs.ind==isneg)?1:0))  // same sign, 0 non-negative
                mult=1;
            else {
                        mult=-1; // will cause subtract
                        /* Before we can subtract we must determine which is the larger,
                           as our add/subtract routine only handles non-negative results
                           so we may need to swap the operands. */

                                if (rhs.ind==iszero){
                                    // original A bigger
                                }else if ((usellen<userlen)|(lhs.ind==iszero))
                                    { // original B bigger
                                        t=usel;
                                        usel=user;
                                        user=t; // swap
                                        tlen=usellen;
                                        usellen=userlen;
                                        userlen=tlen; // ..
                                        res.ind=(sbyte)-res.ind; // and set sign
                                    }
                                else if (usellen>userlen){
                                    // original A bigger
                                }else{
                                    {/* logical lengths the same */ // need compare
                                        /* may still need to swap: compare the strings */
                                        ia=0;
                                        ib=0;
                                        ea=usel.Length-1;
                                        eb=user.Length-1;
                                        for(;;){
                                                if (ia<=ea)
                                                    ca=usel[ia];
                                                else
                                                    {
                                                        if (ib>eb)
                                                            {/* identical */
                                                                if (set.form!=MathContext.PLAIN)
                                                                    return ZERO;
                                                                // [if PLAIN we must do the subtract, in case of 0.000 results]
                                                                break;
                                                            }
                                                        ca=(sbyte)0;
                                                    }
                                                if (ib<=eb)
                                                    cb=user[ib];
                                                else
                                                    cb=(sbyte)0;
                                                if (ca!=cb)
                                                    {
                                                        if (ca<cb)
                                                            {/* swap needed */
                                                                t=usel;
                                                                usel=user;
                                                                user=t; // swap
                                                                tlen=usellen;
                                                                usellen=userlen;
                                                                userlen=tlen; // ..
                                                                res.ind=(sbyte)-res.ind;
                                                            }
                                                        break;
                                                    }
                                                /* mantissas the same, so far */
                                                ia++;
                                                ib++;
                                            }
                                    } // lengths the same
                                }
            }
            res.mant=byteaddsub(usel,usellen,user,userlen,mult,false);
            return res.finish(set,false);
        }
Beispiel #3
0
        public BigDecimal multiply(BigDecimal rhs,MathContext set)
        {
            BigDecimal lhs;
            int padding;
            int reqdig;
            sbyte[] multer=null;
            sbyte[] multand=null;
            int multandlen;
            int acclen=0;
            BigDecimal res;
            sbyte[] acc;
            int n=0;
            int ix;
            sbyte mult=0;
            if (set.lostDigits)
                checkdigits(rhs,set.digits);
            lhs=this; // name for clarity and proxy

            /* Prepare numbers (truncate, unless unlimited precision) */
            padding=0; // trailing 0's to add
            reqdig=set.digits; // local copy
            if (reqdig>0)
                {
                    if (lhs.mant.Length>reqdig)
                        lhs=clone(lhs).round(set);
                    if (rhs.mant.Length>reqdig)
                        rhs=clone(rhs).round(set);
                    // [we could reuse the new LHS for result in this case]
                }
            else
                {/* unlimited */
                    // fixed point arithmetic will want every trailing 0; we add these
                    // after the calculation rather than before, for speed.
                    if (lhs.exp>0)
                        padding=padding+lhs.exp;
                    if (rhs.exp>0)
                        padding=padding+rhs.exp;
                }

            // For best speed, as in DMSRCN, we use the shorter number as the
            // multiplier and the longer as the multiplicand.
            // 1999.12.22: We used to special case when the result would fit in
            //             a long, but with Java 1.3 this gave no advantage.
            if (lhs.mant.Length<rhs.mant.Length)
                {
                    multer=lhs.mant;
                    multand=rhs.mant;
                }
            else
                {
                    multer=rhs.mant;
                    multand=lhs.mant;
                }

            /* Calculate how long result sbyte array will be */
            multandlen=(multer.Length+multand.Length)-1; // effective length
            // optimize for 75% of the cases where a carry is expected...
            if ((multer[0]*multand[0])>9)
                acclen=multandlen+1;
            else
                acclen=multandlen;

            /* Now the main long multiplication loop */
            res=new BigDecimal(); // where we'll build result
            acc=new sbyte[acclen]; // accumulator, all zeros
            for(ix=multer.Length,n=0;ix>0;ix--,n++){
                    mult=multer[n];
                    if (mult!=0)
                        { // [optimization]
                            // accumulate [accumulator is reusable array]
                            acc=byteaddsub(acc,acc.Length,multand,multandlen,mult,true);
                        }
                    // divide multiplicand by 10 for next digit to right
                    multandlen--; // 'virtual length'
                }

            res.ind=(sbyte)(lhs.ind*rhs.ind); // final sign
            res.exp=(lhs.exp+rhs.exp)-padding; // final exponent
            // [overflow is checked by finish]

            /* add trailing zeros to the result, if necessary */
            if (padding==0)
                res.mant=acc;
            else
                res.mant=extend(acc,acc.Length+padding); // add trailing 0s
            return res.finish(set,false);
        }