private void smallRound(MathContext mc, int discardedPrecision) { long sizeOfFraction = LONG_TEN_POW[discardedPrecision]; long newScale = (long)_scale - discardedPrecision; long unscaledVal = smallValue; // Getting the integer part and the discarded fraction long integer = unscaledVal / sizeOfFraction; long fraction = unscaledVal % sizeOfFraction; int compRem; // If the discarded fraction is non-zero perform rounding if (fraction != 0) { // To check if the discarded fraction >= 0.5 compRem = longCompareTo(Math.Abs(fraction) << 1,sizeOfFraction); // To look if there is a carry integer += roundingBehavior( ((int)integer) & 1, Math.Sign(fraction) * (5 + compRem), mc.getRoundingMode()); // If after to add the increment the precision changed, we normalize the size if (Math.Log10(Math.Abs(integer)) >= mc.getPrecision()) { integer /= 10; newScale--; } } // To update all internal fields _scale = toIntScale(newScale); _precision = mc.getPrecision(); smallValue = integer; _bitLength = bitLength(integer); intVal = null; }
public BigDecimal pow(int n, MathContext mc) { // The ANSI standard X3.274-1996 algorithm int m = Math.Abs(n); int mcPrecision = mc.getPrecision(); int elength = (int)Math.Log10(m) + 1; // decimal digits in 'n' int oneBitMask; // mask of bits BigDecimal accum; // the single accumulator MathContext newPrecision = mc; // MathContext by default // In particular cases, it reduces the problem to call the other 'pow()' if ((n == 0) || ((isZero()) && (n > 0))) { return pow(n); } if ((m > 999999999) || ((mcPrecision == 0) && (n < 0)) || ((mcPrecision > 0) && (elength > mcPrecision))) { // math.07=Invalid Operation throw new ArithmeticException("Invalid Operation"); } if (mcPrecision > 0) { newPrecision = new MathContext( mcPrecision + elength + 1, mc.getRoundingMode()); } // The result is calculated as if 'n' were positive accum = round(newPrecision); oneBitMask = highestOneBit(m) >> 1; while (oneBitMask > 0) { accum = accum.multiply(accum, newPrecision); if ((m & oneBitMask) == oneBitMask) { accum = accum.multiply(this, newPrecision); } oneBitMask >>= 1; } // If 'n' is negative, the value is divided into 'ONE' if (n < 0) { accum = ONE.divide(accum, newPrecision); } // The final value is rounded to the destination precision accum.inplaceRound(mc); return accum; }
private void inplaceRound(MathContext mc) { int mcPrecision = mc.getPrecision(); if (aproxPrecision() - mcPrecision <= 0 || mcPrecision == 0) { return; } int discardedPrecision = precision() - mcPrecision; // If no rounding is necessary it returns immediately if ((discardedPrecision <= 0)) { return; } // When the number is small perform an efficient rounding if (this._bitLength < 64) { smallRound(mc, discardedPrecision); return; } // Getting the integer part and the discarded fraction BigInteger sizeOfFraction = Multiplication.powerOf10(discardedPrecision); BigInteger[] integerAndFraction = getUnscaledValue().divideAndRemainder(sizeOfFraction); long newScale = (long)_scale - discardedPrecision; int compRem; BigDecimal tempBD; // If the discarded fraction is non-zero, perform rounding if (integerAndFraction[1].signum() != 0) { // To check if the discarded fraction >= 0.5 compRem = (integerAndFraction[1].abs().shiftLeftOneBit().compareTo(sizeOfFraction)); // To look if there is a carry compRem = roundingBehavior( integerAndFraction[0].testBit(0) ? 1 : 0, integerAndFraction[1].signum() * (5 + compRem), mc.getRoundingMode()); if (compRem != 0) { integerAndFraction[0] = integerAndFraction[0].add(BigInteger.valueOf(compRem)); } tempBD = new BigDecimal(integerAndFraction[0]); // If after to add the increment the precision changed, we normalize the size if (tempBD.precision() > mcPrecision) { integerAndFraction[0] = integerAndFraction[0].divide(BigInteger.TEN); newScale--; } } // To update all internal fields _scale = toIntScale(newScale); _precision = mcPrecision; setUnscaledValue(integerAndFraction[0]); }