001/*
002 * Copyright (C) 2020 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.math;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018import static com.google.common.math.MathPreconditions.checkNonNegative;
019
020import com.google.common.annotations.Beta;
021import com.google.common.annotations.GwtIncompatible;
022import com.google.common.annotations.J2ktIncompatible;
023import java.math.BigDecimal;
024import java.math.BigInteger;
025import java.math.RoundingMode;
026
027/**
028 * A class for arithmetic on {@link BigDecimal} that is not covered by its built-in methods.
029 *
030 * @author Louis Wasserman
031 * @since 30.0
032 */
033@J2ktIncompatible
034@GwtIncompatible
035public final class BigDecimalMath {
036  private BigDecimalMath() {}
037
038  /**
039   * Returns {@code x} converted to a {@link BigInteger}, ensuring that the magnitude of {@code x}
040   * does not exceed {@code maxIntegerDigits} before performing conversion.
041   *
042   * <p>Converting a {@link BigDecimal} with a very large negative scale to a {@code BigInteger}
043   * requires materializing the unscaled value multiplied by {@code 10^-scale}. When {@code x} is
044   * parsed from uncontrolled string or JSON inputs (such as {@code "1e123456789"}), directly
045   * calling {@link BigDecimal#toBigInteger()} or {@link BigDecimal#toBigIntegerExact()} can exhaust
046   * CPU and memory. This method safely verifies that the number of decimal digits required for the
047   * integer representation fits within {@code maxIntegerDigits} before conversion.
048   *
049   * <p>Choosing a reasonable value for {@code maxIntegerDigits} depends on the range of expected
050   * valid values for your input. A value of 100 is generally sufficient for most common use cases
051   * and provides a large safety margin against resource exhaustion from malicious inputs.
052   *
053   * @param x the {@code BigDecimal} to convert to a {@code BigInteger}
054   * @param maxIntegerDigits the maximum allowable number of digits in the integer part (for values
055   *     where {@code |x| < 1}, the integer part is considered to have 0 digits)
056   * @return {@code x} converted to a {@code BigInteger}
057   * @throws IllegalArgumentException if {@code maxIntegerDigits < 0}
058   * @throws ArithmeticException if the integer part of {@code x} requires more than {@code
059   *     maxIntegerDigits} digits
060   * @since 33.7.0
061   */
062  @Beta
063  public static BigInteger toBigInteger(BigDecimal x, int maxIntegerDigits) {
064    checkNotNull(x);
065    checkNonNegative("maxIntegerDigits", maxIntegerDigits);
066    // If x is 0 or |x| < 1 (i.e. precision <= scale), the integer part is BigInteger.ZERO.
067    if (x.signum() == 0 || x.precision() <= x.scale()) {
068      return BigInteger.ZERO;
069    }
070    checkMaxIntegerDigits(x, maxIntegerDigits);
071    return x.toBigInteger();
072  }
073
074  /**
075   * Returns {@code x} converted to a {@link BigInteger}, checking that {@code x} has no fractional
076   * part and ensuring that the magnitude of {@code x} does not exceed {@code maxIntegerDigits}
077   * before performing conversion.
078   *
079   * <p>Converting a {@link BigDecimal} with a very large negative scale to a {@code BigInteger}
080   * requires materializing the unscaled value multiplied by {@code 10^-scale}. When {@code x} is
081   * parsed from uncontrolled string or JSON inputs (such as {@code "1e123456789"}), directly
082   * calling {@link BigDecimal#toBigInteger()} or {@link BigDecimal#toBigIntegerExact()} can exhaust
083   * CPU and memory. This method safely verifies that the number of decimal digits required for the
084   * integer representation fits within {@code maxIntegerDigits} before conversion.
085   *
086   * <p>Choosing a reasonable value for {@code maxIntegerDigits} depends on the range of expected
087   * valid values for your input. A value of 100 is generally sufficient for most common use cases
088   * and provides a large safety margin against resource exhaustion from malicious inputs.
089   *
090   * @param x the {@code BigDecimal} to convert to a {@code BigInteger}
091   * @param maxIntegerDigits the maximum allowable number of digits in the integer part (for values
092   *     where {@code |x| < 1}, the integer part is considered to have 0 digits)
093   * @return {@code x} converted to a {@code BigInteger}
094   * @throws IllegalArgumentException if {@code maxIntegerDigits < 0}
095   * @throws ArithmeticException if {@code x} has a nonzero fractional part or if the integer part
096   *     of {@code x} requires more than {@code maxIntegerDigits} digits
097   * @since 33.7.0
098   */
099  @Beta
100  public static BigInteger toBigIntegerExact(BigDecimal x, int maxIntegerDigits) {
101    checkNotNull(x);
102    checkNonNegative("maxIntegerDigits", maxIntegerDigits);
103    // If x is 0, regardless of scale, the integer value is BigInteger.ZERO.
104    if (x.signum() == 0) {
105      return BigInteger.ZERO;
106    }
107    // If |x| < 1 (i.e. precision <= scale) and x != 0, x has a nonzero fractional part and cannot
108    // be represented exactly as a BigInteger.
109    if (x.precision() <= x.scale()) {
110      throw new ArithmeticException(
111          x + " has a nonzero fractional part and cannot be represented exactly as a BigInteger.");
112    }
113    checkMaxIntegerDigits(x, maxIntegerDigits);
114    return x.toBigIntegerExact();
115  }
116
117  private static void checkMaxIntegerDigits(BigDecimal x, int maxIntegerDigits) {
118    long integerDigits = (long) x.precision() - x.scale();
119    if (integerDigits > maxIntegerDigits) {
120      throw new ArithmeticException(
121          String.format(
122              "BigDecimal (%s) requires %s integer digits, which is > maxIntegerDigits (%s)",
123              x, integerDigits, maxIntegerDigits));
124    }
125  }
126
127  /**
128   * Returns {@code x}, rounded to a {@code double} with the specified rounding mode. If {@code x}
129   * is precisely representable as a {@code double}, its {@code double} value will be returned;
130   * otherwise, the rounding will choose between the two nearest representable values with {@code
131   * mode}.
132   *
133   * <p>For the case of {@link RoundingMode#HALF_DOWN}, {@code HALF_UP}, and {@code HALF_EVEN},
134   * infinite {@code double} values are considered infinitely far away. For example, 2^2000 is not
135   * representable as a double, but {@code roundToDouble(BigDecimal.valueOf(2).pow(2000), HALF_UP)}
136   * will return {@code Double.MAX_VALUE}, not {@code Double.POSITIVE_INFINITY}.
137   *
138   * <p>For the case of {@link RoundingMode#HALF_EVEN}, this implementation uses the IEEE 754
139   * default rounding mode: if the two nearest representable values are equally near, the one with
140   * the least significant bit zero is chosen. (In such cases, both of the nearest representable
141   * values are even integers; this method returns the one that is a multiple of a greater power of
142   * two.)
143   *
144   * @throws ArithmeticException if {@code mode} is {@link RoundingMode#UNNECESSARY} and {@code x}
145   *     is not precisely representable as a {@code double}
146   * @since 30.0
147   */
148  public static double roundToDouble(BigDecimal x, RoundingMode mode) {
149    return BigDecimalToDoubleRounder.INSTANCE.roundToDouble(x, mode);
150  }
151
152  private static final class BigDecimalToDoubleRounder extends ToDoubleRounder<BigDecimal> {
153    static final BigDecimalToDoubleRounder INSTANCE = new BigDecimalToDoubleRounder();
154
155    private BigDecimalToDoubleRounder() {}
156
157    @Override
158    double roundToDoubleArbitrarily(BigDecimal bigDecimal) {
159      return bigDecimal.doubleValue();
160    }
161
162    @Override
163    int sign(BigDecimal bigDecimal) {
164      return bigDecimal.signum();
165    }
166
167    @Override
168    BigDecimal toX(double d, RoundingMode mode) {
169      return new BigDecimal(d);
170    }
171
172    @Override
173    BigDecimal minus(BigDecimal a, BigDecimal b) {
174      return a.subtract(b);
175    }
176  }
177}