< prev index next >
src/java.base/share/classes/java/math/BigDecimal.java
Print this page
@@ -126,10 +126,11 @@
* <tr><th>Operation</th><th>Preferred Scale of Result</th></tr>
* <tr><td>Add</td><td>max(addend.scale(), augend.scale())</td>
* <tr><td>Subtract</td><td>max(minuend.scale(), subtrahend.scale())</td>
* <tr><td>Multiply</td><td>multiplier.scale() + multiplicand.scale()</td>
* <tr><td>Divide</td><td>dividend.scale() - divisor.scale()</td>
+ * <tr><td>Square root</td><td>radicand.scale()/2</td>
* </table>
*
* These scales are the ones used by the methods which return exact
* arithmetic results; except that an exact divide may have to use a
* larger scale since the exact result may have more digits. For
@@ -344,10 +345,20 @@
* @since 1.5
*/
public static final BigDecimal TEN =
ZERO_THROUGH_TEN[10];
+ /**
+ * The value 0.1, with a scale of 1.
+ */
+ private static final BigDecimal ONE_TENTH = valueOf(1L, 1);
+
+ /**
+ * The value 0.5, with a scale of 1.
+ */
+ private static final BigDecimal ONE_HALF = valueOf(5L, 1);
+
// Constructors
/**
* Trusted package private constructor.
* Trusted simply means if val is INFLATED, intVal could not be null and
@@ -1994,10 +2005,299 @@
result[1] = lhs.subtract(result[0].multiply(divisor));
return result;
}
/**
+ * Returns an approximation to the square root of {@code this}
+ * with rounding according to the context settings.
+ *
+ * <p>The preferred scale of the returned result is equal to
+ * {@code this.scale()/2}. The value of the returned result is
+ * always within one ulp of the exact decimal value for the
+ * precision in question. If the rounding mode is {@link
+ * RoundingMode#HALF_UP HALF_UP}, {@link RoundingMode#HALF_DOWN
+ * HALF_DOWN}, or {@link RoundingMode#HALF_EVEN HALF_EVEN}, the
+ * result is within one half an ulp of the exact decimal value.
+ *
+ * <p>Special case:
+ * <ul>
+ * <li> The square root of a number numerically equal to {@code
+ * ZERO} is numerically equal to {@code ZERO} with a preferred
+ * scale according to the general rule above. In particular, for
+ * {@code ZERO}}, {@code ZERO.sqrt(mc).equals(ZERO)} is true with
+ * any {@code MathContext} as an argument.
+ * </ul>
+ *
+ * @param mc the context to use.
+ * @return the square root of {@code this}.
+ * @throws ArithmeticException if {@code this} is less than zero.
+ * @throws ArithmeticException if an exact result is requested
+ * ({@code mc.getPrecision()==0}) and there is no finite decimal
+ * expansion of the exact result
+ * @throws ArithmeticException if
+ * {@code (mc.getRoundingMode()==RoundingMode.UNNECESSARY}) and
+ * the exact result cannot fit in {@code mc.getPrecision()}
+ * digits.
+ * @since 9
+ */
+ public BigDecimal sqrt(MathContext mc) {
+ int signum = signum();
+ if (signum == 1) {
+ /*
+ * The following code draws on the algorithm presented in
+ * "Properly Rounded Variable Precision Square Root," Hull and
+ * Abrham, ACM Transactions on Mathematical Software, Vol 11,
+ * No. 3, September 1985, Pages 229-237.
+ *
+ * The BigDecimal computational model differs from the one
+ * presented in the paper in several ways: first BigDecimal
+ * numbers aren't necessarily normalized, second many more
+ * rounding modes are supported, including UNNECESSARY, and
+ * exact results can be requested.
+ *
+ * The main steps of the algorithm below are as follow, first
+ * argument reduce the value to the numerical range [1, 10)
+ * using the following relations:
+ *
+ * x = y * 10 ^ exp
+ * sqrt(x) = sqrt(y) * 10^(exp / 2) if exp is even
+ * sqrt(x) = sqrt(y/10) * 10 ^((exp+1)/2) is exp is odd
+ *
+ * Then use Newton's iteration on the reduced value to compute
+ * the numerical digits of the desired result.
+ *
+ * Finally, scale back to the desired exponent range and
+ * perform any adjustment to get the preferred scale in the
+ * representation.
+ */
+
+ // The code below favors relative simplicity over checking
+ // for special cases that could run faster.
+
+ int preferredScale = this.scale()/2;
+ BigDecimal zeroWithFinalPreferredScale = valueOf(0L, preferredScale);
+
+ // First phase of numerical normalization, strip trailing
+ // zeros and check for even powers of 10.
+ BigDecimal stripped = this.stripTrailingZeros();
+ int strippedScale = stripped.scale();
+
+ // Numerically sqrt(10^2N) = 10^N
+ if (stripped.isPowerOfTen() &&
+ strippedScale % 2 == 0) {
+ BigDecimal result = valueOf(1L, strippedScale/2);
+ if (result.scale() != preferredScale) {
+ // Adjust to requested precision and preferred
+ // scale as appropriate.
+ result = result.add(zeroWithFinalPreferredScale, mc);
+ }
+ return result;
+ }
+
+ // After stripTrailingZeros, the representation is normalized as
+ //
+ // unscaledValue * 10^(-scale)
+ //
+ // where unscaledValue is an integer with the mimimum
+ // precision for the cohort of the numerical value. To
+ // allow binary floating-point hardware to be used to get
+ // approximately a 15 digit approximation to the square
+ // root, it is helpful to instead normalize this as so
+ // that the significand portion is to right of the decimal
+ // point by roughly (scale() - precision() +1).
+
+ // Now the precision / scale adjustment
+ int scaleAdjust = 0;
+ int scale = stripped.scale() - stripped.precision() + 1;
+ if (scale % 2 == 0) {
+ scaleAdjust = scale;
+ } else {
+ scaleAdjust = scale - 1;
+ }
+
+ BigDecimal working = stripped.scaleByPowerOfTen(scaleAdjust);
+
+ assert // Verify 0.1 <= working < 10
+ ONE_TENTH.compareTo(working) <= 0 && working.compareTo(TEN) < 0;
+
+ // Use good ole' Math.sqrt to get the initial guess for
+ // the Newton iteration, good to at least 15 decimal
+ // digits. This approach does incur the cost of a
+ //
+ // BigDecimal -> double -> BigDecimal
+ //
+ // conversion cycle, but it avoids the need for several
+ // Newton iterations in BigDecimal arithmetic to get the
+ // working answer to 15 digits of precision. If many fewer
+ // than 15 digits were needed, it might be faster to do
+ // the loop entirely in BigDecimal arithmetic.
+ //
+ // (A double value might have as much many as 17 decimal
+ // digits of precision; it depends on the relative density
+ // of binary and decimal numbers at different regions of
+ // the number line.)
+ //
+ // (It would be possible to check for certain special
+ // cases to avoid doing any Newton iterations. For
+ // example, if the BigDecimal -> double conversion was
+ // known to be exact and the rounding mode had a
+ // low-enough precision, the post-Newton rounding logic
+ // could be applied directly.)
+
+ BigDecimal guess = new BigDecimal(Math.sqrt(working.doubleValue()));
+ int guessPrecision = 15;
+ int originalPrecision = mc.getPrecision();
+ int targetPrecision;
+
+ // If an exact value is requested, it must only need about
+ // half of the input digits to represent since multiplying
+ // an N digit number by itself yield a 2N-1 digit or 2N
+ // digit result.
+ if (originalPrecision == 0) {
+ targetPrecision = stripped.precision()/2 + 1;
+ } else {
+ targetPrecision = originalPrecision;
+ }
+
+ // When setting the precision to use inside the Newton
+ // iteration loop, take care to avoid the case where the
+ // precision of the input exceeds the requested precision
+ // and rounding the input value too soon.
+ BigDecimal approx = guess;
+ int workingPrecision = working.precision();
+ do {
+ int tmpPrecision = Math.max(Math.max(guessPrecision, targetPrecision + 2),
+ workingPrecision);
+ MathContext mcTmp = new MathContext(tmpPrecision, RoundingMode.HALF_EVEN);
+ // approx = 0.5 * (approx + fraction / approx)
+ approx = ONE_HALF.multiply(approx.add(working.divide(approx, mcTmp), mcTmp));
+ guessPrecision *= 2;
+ } while (guessPrecision < targetPrecision + 2);
+
+ BigDecimal result;
+ RoundingMode targetRm = mc.getRoundingMode();
+ if (targetRm == RoundingMode.UNNECESSARY || originalPrecision == 0) {
+ RoundingMode tmpRm =
+ (targetRm == RoundingMode.UNNECESSARY) ? RoundingMode.DOWN : targetRm;
+ MathContext mcTmp = new MathContext(targetPrecision, tmpRm);
+ result = approx.scaleByPowerOfTen(-scaleAdjust/2).round(mcTmp);
+
+ // If result*result != this numerically, the square
+ // root isn't exact
+ if (this.subtract(result.multiply(result)).compareTo(ZERO) != 0) {
+ throw new ArithmeticException("Computed square root not exact.");
+ }
+ } else {
+ result = approx.scaleByPowerOfTen(-scaleAdjust/2).round(mc);
+ }
+
+ if (result.scale() != preferredScale) {
+ // The preferred scale of an add is
+ // max(addend.scale(), augend.scale()). Therefore, if
+ // the scale of the result is first minimized using
+ // stripTrailingZeros(), adding a zero of the
+ // preferred scale rounding the correct precision will
+ // perform the proper scale vs precision tradeoffs.
+ result = result.stripTrailingZeros().
+ add(zeroWithFinalPreferredScale,
+ new MathContext(originalPrecision, RoundingMode.UNNECESSARY));
+ }
+ assert squareRootResultAssertions(result, mc);
+ return result;
+ } else {
+ switch (signum) {
+ case -1:
+ throw new ArithmeticException("Attempted square root " +
+ "of negative BigDecimal");
+ case 0:
+ return valueOf(0L, scale()/2);
+
+ default:
+ throw new AssertionError("Bad value from signum");
+ }
+ }
+ }
+
+ private boolean isPowerOfTen() {
+ return BigInteger.ONE.equals(this.unscaledValue());
+ }
+
+ /**
+ * For nonzero values, check numerical correctness properties of
+ * the computed result for the chosen rounding mode .
+ *
+ * For the directed roundings, for DOWN and FLOOR, result^2 must
+ * be {@code <=} the input and (result+ulp)^2 must be {@code >} the
+ * input. Conversely, for UP and CEIL, result^2 must be {@code >=} the
+ * input and (result-ulp)^2 must be {@code <} the input.
+ */
+ private boolean squareRootResultAssertions(BigDecimal result, MathContext mc) {
+ if (result.signum() == 0) {
+ return squareRootZeroResultAssertions(result, mc);
+ } else {
+ RoundingMode rm = mc.getRoundingMode();
+ BigDecimal ulp = result.ulp();
+ BigDecimal neighborUp = result.add(ulp);
+ // Make neighbor down accurate even for powers of ten
+ if (this.isPowerOfTen()) {
+ ulp = ulp.divide(TEN);
+ }
+ BigDecimal neighborDown = result.subtract(ulp);
+
+ // Both the starting value and result should be nonzero and positive.
+ if (result.signum() != 1 ||
+ this.signum() != 1) {
+ return false;
+ }
+
+ switch (rm) {
+ case DOWN:
+ case FLOOR:
+ return
+ result.multiply(result).compareTo(this) <= 0 &&
+ neighborUp.multiply(neighborUp).compareTo(this) > 0;
+
+ case UP:
+ case CEILING:
+ return
+ result.multiply(result).compareTo(this) >= 0 &&
+ neighborDown.multiply(neighborDown).compareTo(this) < 0;
+
+ case HALF_DOWN:
+ case HALF_EVEN:
+ case HALF_UP:
+ BigDecimal err = result.multiply(result).subtract(this).abs();
+ BigDecimal errUp = neighborUp.multiply(neighborUp).subtract(this);
+ BigDecimal errDown = this.subtract(neighborDown.multiply(neighborDown));
+ // All error values should be positive so don't need to
+ // compare absolute values.
+
+ int err_comp_errUp = err.compareTo(errUp);
+ int err_comp_errDown = err.compareTo(errDown);
+
+ return
+ errUp.signum() == 1 &&
+ errDown.signum() == 1 &&
+
+ err_comp_errUp <= 0 &&
+ err_comp_errDown <= 0 &&
+
+ ((err_comp_errUp == 0 ) ? err_comp_errDown < 0 : true) &&
+ ((err_comp_errDown == 0 ) ? err_comp_errUp < 0 : true);
+ // && could check for digit conditions for ties too
+
+ default: // Definition of UNNECESSARY already verified.
+ return true;
+ }
+ }
+ }
+
+ private boolean squareRootZeroResultAssertions(BigDecimal result, MathContext mc) {
+ return this.compareTo(ZERO) == 0;
+ }
+
+ /**
* Returns a {@code BigDecimal} whose value is
* <code>(this<sup>n</sup>)</code>, The power is computed exactly, to
* unlimited precision.
*
* <p>The parameter {@code n} must be in the range 0 through
< prev index next >