aboutsummaryrefslogtreecommitdiff
path: root/guava-tests/test/com/google/common/math/BigDecimalMathTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava-tests/test/com/google/common/math/BigDecimalMathTest.java')
-rw-r--r--guava-tests/test/com/google/common/math/BigDecimalMathTest.java270
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();
+ }
+}