public static BigDecimal Add(BigDecimal value, 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)value.Scale - augend.Scale; // Some operand is zero or the precision is infinity if ((augend.IsZero) || (value.IsZero) || (mc.Precision == 0)) { return(BigMath.Round(Add(value, augend), mc)); } // Cases where there is room for optimizations if (value.AproxPrecision() < diffScale - 1) { larger = augend; smaller = value; } else if (augend.AproxPrecision() < -diffScale - 1) { larger = value; smaller = augend; } else { // No optimization is done return(BigMath.Round(Add(value, augend), mc)); } if (mc.Precision >= larger.AproxPrecision()) { // No optimization is done return(BigMath.Round(Add(value, augend), mc)); } // Cases where it's unnecessary to add two numbers with very different scales var largerSignum = larger.Sign; if (largerSignum == smaller.Sign) { tempBi = Multiplication.MultiplyByPositiveInt(larger.UnscaledValue, 10) + BigInteger.FromInt64(largerSignum); } else { tempBi = larger.UnscaledValue - BigInteger.FromInt64(largerSignum); tempBi = Multiplication.MultiplyByPositiveInt(tempBi, 10) + BigInteger.FromInt64(largerSignum * 9); } // Rounding the improved adding larger = new BigDecimal(tempBi, larger.Scale + 1); return(BigMath.Round(larger, mc)); }
public static BigDecimal Subtract(BigDecimal value, BigDecimal subtrahend, MathContext mc) { if (subtrahend == null) { throw new ArgumentNullException("subtrahend"); } if (mc == null) { throw new ArgumentNullException("mc"); } long diffScale = subtrahend.Scale - (long)value.Scale; // Some operand is zero or the precision is infinity if ((subtrahend.IsZero) || (value.IsZero) || (mc.Precision == 0)) { return(BigMath.Round(Subtract(value, subtrahend), 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.Precision < value.AproxPrecision()) { var thisSignum = value.Sign; BigInteger tempBI; if (thisSignum != subtrahend.Sign) { tempBI = Multiplication.MultiplyByPositiveInt(value.UnscaledValue, 10) + BigInteger.FromInt64(thisSignum); } else { tempBI = value.UnscaledValue - BigInteger.FromInt64(thisSignum); tempBI = Multiplication.MultiplyByPositiveInt(tempBI, 10) + BigInteger.FromInt64(thisSignum * 9); } // Rounding the improved subtracting var leftOperand = new BigDecimal(tempBI, value.Scale + 1); // it will be only the left operand (this) return(BigMath.Round(leftOperand, mc)); } } // No optimization is done return(BigMath.Round(Subtract(value, subtrahend), mc)); }
public static BigDecimal DivideToIntegralValue(BigDecimal dividend, BigDecimal divisor) { BigInteger integralValue; // the integer of result BigInteger powerOfTen; // some power of ten BigInteger quotient; BigInteger remainder; long newScale = (long)dividend.Scale - divisor.Scale; long tempScale = 0; int i = 1; int lastPow = BigDecimal.TenPow.Length - 1; if (divisor.IsZero) { // math.04=Division by zero throw new ArithmeticException(Messages.math04); //$NON-NLS-1$ } if ((divisor.AproxPrecision() + newScale > dividend.AproxPrecision() + 1L) || (dividend.IsZero)) { /* If the divisor's integer part is greater than this's integer part, * the result must be zero with the appropriate scale */ integralValue = BigInteger.Zero; } else if (newScale == 0) { integralValue = dividend.UnscaledValue / divisor.UnscaledValue; } else if (newScale > 0) { powerOfTen = Multiplication.PowerOf10(newScale); integralValue = dividend.UnscaledValue / (divisor.UnscaledValue * powerOfTen); integralValue = integralValue * powerOfTen; } else { // (newScale < 0) powerOfTen = Multiplication.PowerOf10(-newScale); integralValue = (dividend.UnscaledValue * powerOfTen) / divisor.UnscaledValue; // To strip trailing zeros approximating to the preferred scale while (!BigInteger.TestBit(integralValue, 0)) { quotient = BigMath.DivideAndRemainder(integralValue, BigDecimal.TenPow[i], out remainder); if ((remainder.Sign == 0) && (tempScale - i >= newScale)) { tempScale -= i; if (i < lastPow) { i++; } integralValue = quotient; } else { if (i == 1) { break; } i = 1; } } newScale = tempScale; } return((integralValue.Sign == 0) ? BigDecimal.GetZeroScaledBy(newScale) : new BigDecimal(integralValue, BigDecimal.ToIntScale(newScale))); }
public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor, MathContext mc) { /* Calculating how many zeros must be append to 'dividend' * to obtain a quotient with at least 'mc.precision()' digits */ long traillingZeros = mc.Precision + 2L + divisor.AproxPrecision() - dividend.AproxPrecision(); long diffScale = (long)dividend.Scale - divisor.Scale; long newScale = diffScale; // scale of the final quotient int compRem; // to compare the remainder int i = 1; // index int lastPow = BigDecimal.TenPow.Length - 1; // last power of ten BigInteger integerQuot; // for temporal results BigInteger quotient = dividend.UnscaledValue; BigInteger remainder; // In special cases it reduces the problem to call the dual method if ((mc.Precision == 0) || (dividend.IsZero) || (divisor.IsZero)) { return(Divide(dividend, divisor)); } if (traillingZeros > 0) { // To append trailing zeros at end of dividend quotient = dividend.UnscaledValue * Multiplication.PowerOf10(traillingZeros); newScale += traillingZeros; } quotient = BigMath.DivideAndRemainder(quotient, divisor.UnscaledValue, out remainder); integerQuot = quotient; // Calculating the exact quotient with at least 'mc.precision()' digits if (remainder.Sign != 0) { // Checking if: 2 * remainder >= divisor ? compRem = remainder.ShiftLeftOneBit().CompareTo(divisor.UnscaledValue); // quot := quot * 10 + r; with 'r' in {-6,-5,-4, 0,+4,+5,+6} integerQuot = (integerQuot * BigInteger.Ten) + BigInteger.FromInt64(quotient.Sign * (5 + compRem)); newScale++; } else { // To strip trailing zeros until the preferred scale is reached while (!BigInteger.TestBit(integerQuot, 0)) { quotient = BigMath.DivideAndRemainder(integerQuot, BigDecimal.TenPow[i], out remainder); if ((remainder.Sign == 0) && (newScale - i >= diffScale)) { newScale -= i; if (i < lastPow) { i++; } integerQuot = quotient; } else { if (i == 1) { break; } i = 1; } } } // To perform rounding return(new BigDecimal(integerQuot, BigDecimal.ToIntScale(newScale), mc)); }