/* * Copyright (C) 2011 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.math.MathTesting.ALL_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES; import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES; import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES; import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static java.math.BigInteger.ONE; import static java.math.BigInteger.TEN; import static java.math.BigInteger.ZERO; 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 static java.util.Arrays.asList; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.testing.NullPointerTester; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.util.EnumMap; import java.util.EnumSet; import java.util.Map; import junit.framework.TestCase; /** * Tests for BigIntegerMath. * * @author Louis Wasserman */ @GwtCompatible(emulated = true) public class BigIntegerMathTest extends TestCase { public void testCeilingPowerOfTwo() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger result = BigIntegerMath.ceilingPowerOfTwo(x); assertTrue(BigIntegerMath.isPowerOfTwo(result)); assertTrue(result.compareTo(x) >= 0); assertTrue(result.compareTo(x.add(x)) < 0); } } public void testFloorPowerOfTwo() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger result = BigIntegerMath.floorPowerOfTwo(x); assertTrue(BigIntegerMath.isPowerOfTwo(result)); assertTrue(result.compareTo(x) <= 0); assertTrue(result.add(result).compareTo(x) > 0); } } public void testCeilingPowerOfTwoNegative() { for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { try { BigIntegerMath.ceilingPowerOfTwo(x); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } public void testFloorPowerOfTwoNegative() { for (BigInteger x : NEGATIVE_BIGINTEGER_CANDIDATES) { try { BigIntegerMath.floorPowerOfTwo(x); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } public void testCeilingPowerOfTwoZero() { try { BigIntegerMath.ceilingPowerOfTwo(BigInteger.ZERO); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } public void testFloorPowerOfTwoZero() { try { BigIntegerMath.floorPowerOfTwo(BigInteger.ZERO); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @GwtIncompatible // TODO public void testConstantSqrt2PrecomputedBits() { assertEquals( BigIntegerMath.sqrt( BigInteger.ZERO.setBit(2 * BigIntegerMath.SQRT2_PRECOMPUTE_THRESHOLD + 1), FLOOR), BigIntegerMath.SQRT2_PRECOMPUTED_BITS); } public void testIsPowerOfTwo() { for (BigInteger x : ALL_BIGINTEGER_CANDIDATES) { // Checks for a single bit set. boolean expected = x.signum() > 0 & x.and(x.subtract(ONE)).equals(ZERO); assertEquals(expected, BigIntegerMath.isPowerOfTwo(x)); } } public void testLog2ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.log2(ZERO, mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } public void testLog2NegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.log2(BigInteger.valueOf(-1), mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } public void testLog2Floor() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(FLOOR, DOWN)) { int result = BigIntegerMath.log2(x, mode); assertTrue(ZERO.setBit(result).compareTo(x) <= 0); assertTrue(ZERO.setBit(result + 1).compareTo(x) > 0); } } } public void testLog2Ceiling() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(CEILING, UP)) { int result = BigIntegerMath.log2(x, mode); assertTrue(ZERO.setBit(result).compareTo(x) >= 0); assertTrue(result == 0 || ZERO.setBit(result - 1).compareTo(x) < 0); } } } // Relies on the correctness of isPowerOfTwo(BigInteger). public void testLog2Exact() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { // We only expect an exception if x was not a power of 2. boolean isPowerOf2 = BigIntegerMath.isPowerOfTwo(x); try { assertEquals(x, ZERO.setBit(BigIntegerMath.log2(x, UNNECESSARY))); assertTrue(isPowerOf2); } catch (ArithmeticException e) { assertFalse(isPowerOf2); } } } public void testLog2HalfUp() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int result = BigIntegerMath.log2(x, HALF_UP); BigInteger x2 = x.pow(2); // x^2 < 2^(2 * result + 1), or else we would have rounded up assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) > 0); // x^2 >= 2^(2 * result - 1), or else we would have rounded down assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) <= 0); } } public void testLog2HalfDown() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int result = BigIntegerMath.log2(x, HALF_DOWN); BigInteger x2 = x.pow(2); // x^2 <= 2^(2 * result + 1), or else we would have rounded up assertTrue(ZERO.setBit(2 * result + 1).compareTo(x2) >= 0); // x^2 > 2^(2 * result - 1), or else we would have rounded down assertTrue(result == 0 || ZERO.setBit(2 * result - 1).compareTo(x2) < 0); } } // Relies on the correctness of log2(BigInteger, {HALF_UP,HALF_DOWN}). public void testLog2HalfEven() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int halfEven = BigIntegerMath.log2(x, HALF_EVEN); // Now figure out what rounding mode we should behave like (it depends if FLOOR was // odd/even). boolean floorWasEven = (BigIntegerMath.log2(x, FLOOR) & 1) == 0; assertEquals(BigIntegerMath.log2(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); } } @GwtIncompatible // TODO public void testLog10ZeroAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.log10(ZERO, mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } @GwtIncompatible // TODO public void testLog10NegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.log10(BigInteger.valueOf(-1), mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } @GwtIncompatible // TODO public void testLog10Floor() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(FLOOR, DOWN)) { int result = BigIntegerMath.log10(x, mode); assertTrue(TEN.pow(result).compareTo(x) <= 0); assertTrue(TEN.pow(result + 1).compareTo(x) > 0); } } } @GwtIncompatible // TODO public void testLog10Ceiling() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(CEILING, UP)) { int result = BigIntegerMath.log10(x, mode); assertTrue(TEN.pow(result).compareTo(x) >= 0); assertTrue(result == 0 || TEN.pow(result - 1).compareTo(x) < 0); } } } // Relies on the correctness of log10(BigInteger, FLOOR). @GwtIncompatible // TODO public void testLog10Exact() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int logFloor = BigIntegerMath.log10(x, FLOOR); boolean expectSuccess = TEN.pow(logFloor).equals(x); try { assertEquals(logFloor, BigIntegerMath.log10(x, UNNECESSARY)); assertTrue(expectSuccess); } catch (ArithmeticException e) { assertFalse(expectSuccess); } } } @GwtIncompatible // TODO public void testLog10HalfUp() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int result = BigIntegerMath.log10(x, HALF_UP); BigInteger x2 = x.pow(2); // x^2 < 10^(2 * result + 1), or else we would have rounded up assertTrue(TEN.pow(2 * result + 1).compareTo(x2) > 0); // x^2 >= 10^(2 * result - 1), or else we would have rounded down assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) <= 0); } } @GwtIncompatible // TODO public void testLog10HalfDown() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int result = BigIntegerMath.log10(x, HALF_DOWN); BigInteger x2 = x.pow(2); // x^2 <= 10^(2 * result + 1), or else we would have rounded up assertTrue(TEN.pow(2 * result + 1).compareTo(x2) >= 0); // x^2 > 10^(2 * result - 1), or else we would have rounded down assertTrue(result == 0 || TEN.pow(2 * result - 1).compareTo(x2) < 0); } } // Relies on the correctness of log10(BigInteger, {HALF_UP,HALF_DOWN}). @GwtIncompatible // TODO public void testLog10HalfEven() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { int halfEven = BigIntegerMath.log10(x, HALF_EVEN); // Now figure out what rounding mode we should behave like (it depends if FLOOR was // odd/even). boolean floorWasEven = (BigIntegerMath.log10(x, FLOOR) & 1) == 0; assertEquals(BigIntegerMath.log10(x, floorWasEven ? HALF_DOWN : HALF_UP), halfEven); } } @GwtIncompatible // TODO public void testLog10TrivialOnPowerOf10() { BigInteger x = BigInteger.TEN.pow(100); for (RoundingMode mode : ALL_ROUNDING_MODES) { assertEquals(100, BigIntegerMath.log10(x, mode)); } } @GwtIncompatible // TODO public void testSqrtZeroAlwaysZero() { for (RoundingMode mode : ALL_ROUNDING_MODES) { assertEquals(ZERO, BigIntegerMath.sqrt(ZERO, mode)); } } @GwtIncompatible // TODO public void testSqrtNegativeAlwaysThrows() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.sqrt(BigInteger.valueOf(-1), mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } @GwtIncompatible // TODO public void testSqrtFloor() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(FLOOR, DOWN)) { BigInteger result = BigIntegerMath.sqrt(x, mode); assertTrue(result.compareTo(ZERO) > 0); assertTrue(result.pow(2).compareTo(x) <= 0); assertTrue(result.add(ONE).pow(2).compareTo(x) > 0); } } } @GwtIncompatible // TODO public void testSqrtCeiling() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : asList(CEILING, UP)) { BigInteger result = BigIntegerMath.sqrt(x, mode); assertTrue(result.compareTo(ZERO) > 0); assertTrue(result.pow(2).compareTo(x) >= 0); assertTrue(result.signum() == 0 || result.subtract(ONE).pow(2).compareTo(x) < 0); } } } // Relies on the correctness of sqrt(BigInteger, FLOOR). @GwtIncompatible // TODO public void testSqrtExact() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger floor = BigIntegerMath.sqrt(x, FLOOR); // We only expect an exception if x was not a perfect square. boolean isPerfectSquare = floor.pow(2).equals(x); try { assertEquals(floor, BigIntegerMath.sqrt(x, UNNECESSARY)); assertTrue(isPerfectSquare); } catch (ArithmeticException e) { assertFalse(isPerfectSquare); } } } @GwtIncompatible // TODO public void testSqrtHalfUp() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger result = BigIntegerMath.sqrt(x, HALF_UP); BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); BigInteger x4 = x.shiftLeft(2); // sqrt(x) < result + 0.5, so 4 * x < (result + 0.5)^2 * 4 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 assertTrue(x4.compareTo(plusHalfSquared) < 0); BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) >= 0); } } @GwtIncompatible // TODO public void testSqrtHalfDown() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger result = BigIntegerMath.sqrt(x, HALF_DOWN); BigInteger plusHalfSquared = result.pow(2).add(result).shiftLeft(2).add(ONE); BigInteger x4 = x.shiftLeft(2); // sqrt(x) <= result + 0.5, so 4 * x <= (result + 0.5)^2 * 4 // (result + 0.5)^2 * 4 = (result^2 + result)*4 + 1 assertTrue(x4.compareTo(plusHalfSquared) <= 0); BigInteger minusHalfSquared = result.pow(2).subtract(result).shiftLeft(2).add(ONE); // sqrt(x) > result - 0.5, so 4 * x > (result - 0.5)^2 * 4 // (result - 0.5)^2 * 4 = (result^2 - result)*4 + 1 assertTrue(result.equals(ZERO) || x4.compareTo(minusHalfSquared) > 0); } } // Relies on the correctness of sqrt(BigInteger, {HALF_UP,HALF_DOWN}). @GwtIncompatible // TODO public void testSqrtHalfEven() { for (BigInteger x : POSITIVE_BIGINTEGER_CANDIDATES) { BigInteger halfEven = BigIntegerMath.sqrt(x, HALF_EVEN); // Now figure out what rounding mode we should behave like (it depends if FLOOR was // odd/even). boolean floorWasOdd = BigIntegerMath.sqrt(x, FLOOR).testBit(0); assertEquals(BigIntegerMath.sqrt(x, floorWasOdd ? HALF_UP : HALF_DOWN), halfEven); } } @GwtIncompatible // TODO @AndroidIncompatible // slow public void testDivNonZero() { for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigInteger expected = new BigDecimal(p).divide(new BigDecimal(q), 0, mode).toBigIntegerExact(); assertEquals(expected, BigIntegerMath.divide(p, q, mode)); } } } } private static final BigInteger BAD_FOR_ANDROID_P = new BigInteger("-9223372036854775808"); private static final BigInteger BAD_FOR_ANDROID_Q = new BigInteger("-1"); private static final BigInteger BAD_FOR_GINGERBREAD_P = new BigInteger("-9223372036854775808"); private static final BigInteger BAD_FOR_GINGERBREAD_Q = new BigInteger("-4294967296"); @GwtIncompatible // TODO @AndroidIncompatible // slow public void testDivNonZeroExact() { boolean isAndroid = System.getProperties().getProperty("java.runtime.name").contains("Android"); for (BigInteger p : NONZERO_BIGINTEGER_CANDIDATES) { for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { if (isAndroid && p.equals(BAD_FOR_ANDROID_P) && q.equals(BAD_FOR_ANDROID_Q)) { // https://code.google.com/p/android/issues/detail?id=196555 continue; } if (isAndroid && p.equals(BAD_FOR_GINGERBREAD_P) && q.equals(BAD_FOR_GINGERBREAD_Q)) { // Works fine under Marshmallow, so I haven't filed a bug. continue; } boolean dividesEvenly = p.remainder(q).equals(ZERO); try { BigInteger quotient = BigIntegerMath.divide(p, q, UNNECESSARY); BigInteger undone = quotient.multiply(q); if (!p.equals(undone)) { failFormat("expected %s.multiply(%s) = %s; got %s", quotient, q, p, undone); } assertTrue(dividesEvenly); } catch (ArithmeticException e) { assertFalse(dividesEvenly); } } } } @GwtIncompatible // TODO public void testZeroDivIsAlwaysZero() { for (BigInteger q : NONZERO_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { assertEquals(ZERO, BigIntegerMath.divide(ZERO, q, mode)); } } } @GwtIncompatible // TODO public void testDivByZeroAlwaysFails() { for (BigInteger p : ALL_BIGINTEGER_CANDIDATES) { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { BigIntegerMath.divide(p, ZERO, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } } public void testFactorial() { BigInteger expected = BigInteger.ONE; for (int i = 1; i <= 200; i++) { expected = expected.multiply(BigInteger.valueOf(i)); assertEquals(expected, BigIntegerMath.factorial(i)); } } public void testFactorial0() { assertEquals(BigInteger.ONE, BigIntegerMath.factorial(0)); } public void testFactorialNegative() { try { BigIntegerMath.factorial(-1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } public void testBinomialSmall() { runBinomialTest(0, 30); } @GwtIncompatible // too slow public void testBinomialLarge() { runBinomialTest(31, 100); } // Depends on the correctness of BigIntegerMath.factorial private static void runBinomialTest(int firstN, int lastN) { for (int n = firstN; n <= lastN; n++) { for (int k = 0; k <= n; k++) { BigInteger expected = BigIntegerMath.factorial(n) .divide(BigIntegerMath.factorial(k)) .divide(BigIntegerMath.factorial(n - k)); assertEquals(expected, BigIntegerMath.binomial(n, k)); } } } public void testBinomialOutside() { for (int n = 0; n <= 50; n++) { try { BigIntegerMath.binomial(n, -1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } try { BigIntegerMath.binomial(n, n + 1); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } @GwtIncompatible private static final class RoundToDoubleTester { private final BigInteger input; private final Map expectedValues = new EnumMap<>(RoundingMode.class); private boolean unnecessaryShouldThrow = false; RoundToDoubleTester(BigInteger 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 entry : expectedValues.entrySet()) { RoundingMode mode = entry.getKey(); Double expectation = entry.getValue(); assertWithMessage("roundToDouble(" + input + ", " + mode + ")") .that(BigIntegerMath.roundToDouble(input, mode)) .isEqualTo(expectation); } if (!expectedValues.containsKey(UNNECESSARY)) { assertWithMessage("Expected roundUnnecessaryShouldThrow call") .that(unnecessaryShouldThrow) .isTrue(); try { BigIntegerMath.roundToDouble(input, UNNECESSARY); fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); } catch (ArithmeticException expected) { // expected } } } } @GwtIncompatible public void testRoundToDouble_Zero() { new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test(); } @GwtIncompatible public void testRoundToDouble_smallPositive() { new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test(); } @GwtIncompatible public void testRoundToDouble_maxPreciselyRepresentable() { new RoundToDoubleTester(BigInteger.valueOf(1L << 53)) .setExpectation(Math.pow(2, 53), values()) .test(); } @GwtIncompatible 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(BigInteger.valueOf((1L << 53) + 1)) .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN) .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP) .roundUnnecessaryShouldThrow() .test(); } @GwtIncompatible 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(BigInteger.valueOf((1L << 54) + 1)) .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) .roundUnnecessaryShouldThrow() .test(); } @GwtIncompatible 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(BigInteger.valueOf((1L << 54) + 3)) .setExpectation(twoToThe54, DOWN, FLOOR) .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN) .roundUnnecessaryShouldThrow() .test(); } @GwtIncompatible public void testRoundToDouble_twoToThe54PlusFour() { new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4)) .setExpectation(Math.pow(2, 54) + 4, values()) .test(); } @GwtIncompatible public void testRoundToDouble_maxDouble() { BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test(); } @GwtIncompatible public void testRoundToDouble_maxDoublePlusOne() { BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE); new RoundToDoubleTester(maxDoubleAsBI) .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) .roundUnnecessaryShouldThrow() .test(); } @GwtIncompatible public void testRoundToDouble_wayTooBig() { BigInteger bi = BigInteger.ONE.shiftLeft(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(); } @GwtIncompatible public void testRoundToDouble_smallNegative() { new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test(); } @GwtIncompatible public void testRoundToDouble_minPreciselyRepresentable() { new RoundToDoubleTester(BigInteger.valueOf(-1L << 53)) .setExpectation(-Math.pow(2, 53), values()) .test(); } @GwtIncompatible 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(BigInteger.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(); } @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusOne() { new RoundToDoubleTester(BigInteger.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(); } @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusThree() { new RoundToDoubleTester(BigInteger.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(); } @GwtIncompatible public void testRoundToDouble_negativeTwoToThe54MinusFour() { new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4)) .setExpectation(-Math.pow(2, 54) - 4, values()) .test(); } @GwtIncompatible public void testRoundToDouble_minDouble() { BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test(); } @GwtIncompatible public void testRoundToDouble_minDoubleMinusOne() { BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE); new RoundToDoubleTester(minDoubleAsBI) .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) .roundUnnecessaryShouldThrow() .test(); } @GwtIncompatible public void testRoundToDouble_negativeWayTooBig() { BigInteger bi = BigInteger.ONE.shiftLeft(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(); } @GwtIncompatible // NullPointerTester public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); tester.setDefault(BigInteger.class, ONE); tester.setDefault(int.class, 1); tester.setDefault(long.class, 1L); tester.testAllPublicStaticMethods(BigIntegerMath.class); } @GwtIncompatible // String.format private static void failFormat(String template, Object... args) { fail(String.format(template, args)); } }