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}