public static MPUnit Sub(MPUnit minuend, MPUnit subtrahend) { // subtrahend >= minuend int compares = minuend.CompareTo(subtrahend); if(compares == 0) { MPUnit retval = new MPUnit(); retval.numDigits = minuend.numDigits; return retval; } if(compares<0) throw new ArithmeticException(); MPUnit difference = new MPUnit(); int maxdigits = minuend.Digits; subtrahend.Digits=maxdigits; int carry=0; for(int i=0; i<maxdigits; i++) { int resdigit = minuend[i]-subtrahend[i]+carry; if(resdigit<0) { resdigit+=MPUnit.BASE; carry=-1; } else carry=0; difference[i]=(UInt16)resdigit; } while(difference.Digits >0 && difference[difference.Digits-1]==0) { difference.Digits = difference.Digits-1; } // underflow exception if(carry==-1) throw new ArithmeticException(); return difference; }
/// <summary> /// Divides current MPUnit by mpu1, Quotient in q, Remainder in r /// </summary> /// <param name="mpu1"></param> /// <param name="q"></param> /// <param name="r"></param> /// <returns></returns> public static void Div(MPUnit dividend, MPUnit divisor, ref MPUnit q, ref MPUnit r) { MPUnit mpuInter = new MPUnit(); MPUnit u = new MPUnit(dividend); MPUnit v = new MPUnit(divisor); q = new MPUnit(); // Can't divide by zero if(v.numDigits == 0) throw new ArgumentException(); // dividing zero by something else equals zero if(u.numDigits == 0) { q.numDigits=0; r.numDigits=0; return; } // if the v only has one digit, use the simple routine // BUGBUG, how about pretending v has two digits, then postshifting? if(v.numDigits == 1) { Div(u, v[0], ref q , ref r); return; } // ok, long route // normalize to give better qhat estimates // this raises number of digits in u by one // (top digit may be zero) and does not raise the number // of digits in v (since we've just scaled its top // digit to be between BASE/2 and BASE UInt16 scale = 1; int n = v.Digits; int m = u.Digits-n; int v_msd = v[n-1]; // scale up v while(v_msd < BASE/2) { v_msd <<= 1; scale <<= 1; } // if no shift occurs, or if the multiplication // doesn't cause a carry into a higher digit // we will add an additional 0 digit anyway int u_inc_digits = u.Digits+1; if(scale != 1) { // This may or may not increment the number of digits in u... // must check this int digits = u.Digits; u = MPUnit.Mult(u,scale); v = MPUnit.Mult(v,scale); } u.Digits=u_inc_digits; // initialize j for(int j=m; j>=0; j--) { // generate qhat // From Knuth (Uj+nB + Uj+n-1)/(Vn-1) long uhat = (((long)u[j+n]) << 16) + ((long)u[j+n-1]) ; long vhat = (long)v[n-1]; long qhat = uhat / vhat ; long rhat = uhat - (qhat * vhat); long test1 = qhat*v[n-2]; long test2 = (BASE * rhat) + ( (j+n-2) >=0 ? (int)u[j+n-2] : (int)0); // Make sure we didn't overflow in // creating the test values Debug.Assert(test1>=0 && test2>=0); // decrease qhat by one if it is BASE or test fails if(qhat == BASE || test1 > test2) { qhat--; rhat += v[n-1]; test1 = qhat*v[n-2]; test2 = (BASE * rhat) + ( (j+n-2) >=0 ? (int)u[j+n-2] : (int)0); // qhat is still 1 too great if(rhat < BASE && (qhat == BASE || test1>test2)) { qhat--; } } Debug.Assert(qhat < BASE && qhat>=0 && rhat>=0); // Multiply and subtract // subtract term from top term.Digits digits of u // easiest done as a shift of term? MPUnit term = MPUnit.Mult(v,(ushort)qhat); term.ASL(j); // if the result would be negative, then // we oopsd again if(u.CompareTo(term)<0) { qhat--; term = MPUnit.Mult(v,(ushort)qhat); term.ASL(j); } u = MPUnit.Sub(u,term); // set quotient digit q[j]=(ushort)qhat; } q.Trim(); r = MPUnit.Sub(dividend,MPUnit.Mult(divisor,q)); r.Trim(); return; }