diff options
Diffstat (limited to 'guava-tests/test/com/google/common/math/BigDecimalMathTest.java')
-rw-r--r-- | guava-tests/test/com/google/common/math/BigDecimalMathTest.java | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/guava-tests/test/com/google/common/math/BigDecimalMathTest.java b/guava-tests/test/com/google/common/math/BigDecimalMathTest.java new file mode 100644 index 000000000..ff86fd52f --- /dev/null +++ b/guava-tests/test/com/google/common/math/BigDecimalMathTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2020 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.common.math; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; +import static java.math.RoundingMode.CEILING; +import static java.math.RoundingMode.DOWN; +import static java.math.RoundingMode.FLOOR; +import static java.math.RoundingMode.HALF_DOWN; +import static java.math.RoundingMode.HALF_EVEN; +import static java.math.RoundingMode.HALF_UP; +import static java.math.RoundingMode.UNNECESSARY; +import static java.math.RoundingMode.UP; +import static java.math.RoundingMode.values; + +import com.google.common.annotations.GwtIncompatible; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Map; +import junit.framework.TestCase; + +@GwtIncompatible +public class BigDecimalMathTest extends TestCase { + private static final class RoundToDoubleTester { + private final BigDecimal input; + private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class); + private boolean unnecessaryShouldThrow = false; + + RoundToDoubleTester(BigDecimal input) { + this.input = input; + } + + RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { + for (RoundingMode mode : modes) { + Double previous = expectedValues.put(mode, expectedValue); + if (previous != null) { + throw new AssertionError(); + } + } + return this; + } + + public RoundToDoubleTester roundUnnecessaryShouldThrow() { + unnecessaryShouldThrow = true; + return this; + } + + public void test() { + assertThat(expectedValues.keySet()) + .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY))); + for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) { + RoundingMode mode = entry.getKey(); + Double expectation = entry.getValue(); + assertWithMessage("roundToDouble(" + input + ", " + mode + ")") + .that(BigDecimalMath.roundToDouble(input, mode)) + .isEqualTo(expectation); + } + + if (!expectedValues.containsKey(UNNECESSARY)) { + assertWithMessage("Expected roundUnnecessaryShouldThrow call") + .that(unnecessaryShouldThrow) + .isTrue(); + try { + BigDecimalMath.roundToDouble(input, UNNECESSARY); + fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); + } catch (ArithmeticException expected) { + // expected + } + } + } + } + + public void testRoundToDouble_zero() { + new RoundToDoubleTester(BigDecimal.ZERO).setExpectation(0.0, values()).test(); + } + + public void testRoundToDouble_oneThird() { + new RoundToDoubleTester( + BigDecimal.ONE.divide(BigDecimal.valueOf(3), new MathContext(50, HALF_EVEN))) + .roundUnnecessaryShouldThrow() + .setExpectation(0.33333333333333337, UP, CEILING) + .setExpectation(0.3333333333333333, HALF_EVEN, FLOOR, DOWN, HALF_UP, HALF_DOWN) + .test(); + } + + public void testRoundToDouble_halfMinDouble() { + BigDecimal minDouble = new BigDecimal(Double.MIN_VALUE); + BigDecimal halfMinDouble = minDouble.divide(BigDecimal.valueOf(2)); + new RoundToDoubleTester(halfMinDouble) + .roundUnnecessaryShouldThrow() + .setExpectation(Double.MIN_VALUE, UP, CEILING, HALF_UP) + .setExpectation(0.0, HALF_EVEN, FLOOR, DOWN, HALF_DOWN) + .test(); + } + + public void testRoundToDouble_halfNegativeMinDouble() { + BigDecimal minDouble = new BigDecimal(-Double.MIN_VALUE); + BigDecimal halfMinDouble = minDouble.divide(BigDecimal.valueOf(2)); + new RoundToDoubleTester(halfMinDouble) + .roundUnnecessaryShouldThrow() + .setExpectation(-Double.MIN_VALUE, UP, FLOOR, HALF_UP) + .setExpectation(-0.0, HALF_EVEN, CEILING, DOWN, HALF_DOWN) + .test(); + } + + public void testRoundToDouble_smallPositive() { + new RoundToDoubleTester(BigDecimal.valueOf(16)).setExpectation(16.0, values()).test(); + } + + public void testRoundToDouble_maxPreciselyRepresentable() { + new RoundToDoubleTester(BigDecimal.valueOf(1L << 53)) + .setExpectation(Math.pow(2, 53), values()) + .test(); + } + + public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { + double twoToThe53 = Math.pow(2, 53); + // the representable doubles are 2^53 and 2^53 + 2. + // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. + new RoundToDoubleTester(BigDecimal.valueOf((1L << 53) + 1)) + .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN) + .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_twoToThe54PlusOne() { + double twoToThe54 = Math.pow(2, 54); + // the representable doubles are 2^54 and 2^54 + 4 + // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down. + new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 1)) + .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) + .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_twoToThe54PlusOneHalf() { + double twoToThe54 = Math.pow(2, 54); + // the representable doubles are 2^54 and 2^54 + 4 + // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down. + new RoundToDoubleTester(BigDecimal.valueOf(1L << 54).add(new BigDecimal(0.5))) + .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) + .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_twoToThe54PlusThree() { + double twoToThe54 = Math.pow(2, 54); + // the representable doubles are 2^54 and 2^54 + 4 + // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up. + new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 3)) + .setExpectation(twoToThe54, DOWN, FLOOR) + .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_twoToThe54PlusFour() { + new RoundToDoubleTester(BigDecimal.valueOf((1L << 54) + 4)) + .setExpectation(Math.pow(2, 54) + 4, values()) + .test(); + } + + public void testRoundToDouble_maxDouble() { + BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE); + new RoundToDoubleTester(maxDoubleAsBD).setExpectation(Double.MAX_VALUE, values()).test(); + } + + public void testRoundToDouble_maxDoublePlusOne() { + BigDecimal maxDoubleAsBD = new BigDecimal(Double.MAX_VALUE).add(BigDecimal.ONE); + new RoundToDoubleTester(maxDoubleAsBD) + .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) + .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_wayTooBig() { + BigDecimal bi = BigDecimal.valueOf(2).pow(2 * Double.MAX_EXPONENT); + new RoundToDoubleTester(bi) + .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) + .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_smallNegative() { + new RoundToDoubleTester(BigDecimal.valueOf(-16)).setExpectation(-16.0, values()).test(); + } + + public void testRoundToDouble_minPreciselyRepresentable() { + new RoundToDoubleTester(BigDecimal.valueOf(-1L << 53)) + .setExpectation(-Math.pow(2, 53), values()) + .test(); + } + + public void testRoundToDouble_minPreciselyRepresentableMinusOne() { + // the representable doubles are -2^53 and -2^53 - 2. + // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. + new RoundToDoubleTester(BigDecimal.valueOf((-1L << 53) - 1)) + .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN) + .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_negativeTwoToThe54MinusOne() { + new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 1)) + .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN) + .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_negativeTwoToThe54MinusThree() { + new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 3)) + .setExpectation(-Math.pow(2, 54), DOWN, CEILING) + .setExpectation( + DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_negativeTwoToThe54MinusFour() { + new RoundToDoubleTester(BigDecimal.valueOf((-1L << 54) - 4)) + .setExpectation(-Math.pow(2, 54) - 4, values()) + .test(); + } + + public void testRoundToDouble_minDouble() { + BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE); + new RoundToDoubleTester(minDoubleAsBD).setExpectation(-Double.MAX_VALUE, values()).test(); + } + + public void testRoundToDouble_minDoubleMinusOne() { + BigDecimal minDoubleAsBD = new BigDecimal(-Double.MAX_VALUE).subtract(BigDecimal.ONE); + new RoundToDoubleTester(minDoubleAsBD) + .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) + .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) + .roundUnnecessaryShouldThrow() + .test(); + } + + public void testRoundToDouble_negativeWayTooBig() { + BigDecimal bi = BigDecimal.valueOf(2).pow(2 * Double.MAX_EXPONENT).negate(); + new RoundToDoubleTester(bi) + .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) + .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) + .roundUnnecessaryShouldThrow() + .test(); + } +} |