public void testFuzzyEqualsZeroTolerance() { // make sure we test -0 tolerance for (double zero : Doubles.asList(0.0, -0.0)) { for (double a : ALL_DOUBLE_CANDIDATES) { for (double b : ALL_DOUBLE_CANDIDATES) { assertEquals( a == b || (Double.isNaN(a) && Double.isNaN(b)), DoubleMath.fuzzyEquals(a, b, zero)); } } } }
static Map<Imt, Map<Gmm, List<Double>>> initValueMaps(Set<Imt> imts, Set<Gmm> gmms, int size) { Map<Imt, Map<Gmm, List<Double>>> imtMap = Maps.newEnumMap(Imt.class); for (Imt imt : imts) { Map<Gmm, List<Double>> gmmMap = Maps.newEnumMap(Gmm.class); for (Gmm gmm : gmms) { gmmMap.put(gmm, Doubles.asList(new double[size])); } imtMap.put(imt, gmmMap); } return imtMap; }
static { ImmutableSet.Builder<Double> integralBuilder = ImmutableSet.builder(); ImmutableSet.Builder<Double> fractionalBuilder = ImmutableSet.builder(); integralBuilder.addAll(Doubles.asList(0.0, -0.0, Double.MAX_VALUE, -Double.MAX_VALUE)); // Add small multiples of MIN_VALUE and MIN_NORMAL for (int scale = 1; scale <= 4; scale++) { for (double d : Doubles.asList(Double.MIN_VALUE, Double.MIN_NORMAL)) { fractionalBuilder.add(d * scale).add(-d * scale); } } for (double d : Doubles.asList( 0, 1, 2, 7, 51, 102, Math.scalb(1.0, 53), Integer.MIN_VALUE, Integer.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE)) { for (double delta : Doubles.asList(0.0, 1.0, 2.0)) { integralBuilder.addAll(Doubles.asList(d + delta, d - delta, -d - delta, -d + delta)); } for (double delta : Doubles.asList(0.01, 0.1, 0.25, 0.499, 0.5, 0.501, 0.7, 0.8)) { double x = d + delta; if (x != Math.round(x)) { fractionalBuilder.add(x); } } } INTEGRAL_DOUBLE_CANDIDATES = integralBuilder.build(); fractionalBuilder.add(1.414).add(1.415).add(Math.sqrt(2)); fractionalBuilder.add(5.656).add(5.657).add(4 * Math.sqrt(2)); for (double d : INTEGRAL_DOUBLE_CANDIDATES) { double x = 1 / d; if (x != Math.rint(x)) { fractionalBuilder.add(x); } } FRACTIONAL_DOUBLE_CANDIDATES = fractionalBuilder.build(); FINITE_DOUBLE_CANDIDATES = Iterables.concat(FRACTIONAL_DOUBLE_CANDIDATES, INTEGRAL_DOUBLE_CANDIDATES); POSITIVE_FINITE_DOUBLE_CANDIDATES = Iterables.filter( FINITE_DOUBLE_CANDIDATES, new Predicate<Double>() { @Override public boolean apply(Double input) { return input.doubleValue() > 0.0; } }); DOUBLE_CANDIDATES_EXCEPT_NAN = Iterables.concat(FINITE_DOUBLE_CANDIDATES, INFINITIES); ALL_DOUBLE_CANDIDATES = Iterables.concat(DOUBLE_CANDIDATES_EXCEPT_NAN, asList(Double.NaN)); }
/** * Exhaustive input sets for every integral type. * * @author Louis Wasserman */ @GwtCompatible public class MathTesting { static final ImmutableSet<RoundingMode> ALL_ROUNDING_MODES = ImmutableSet.copyOf(RoundingMode.values()); static final ImmutableList<RoundingMode> ALL_SAFE_ROUNDING_MODES = ImmutableList.of(DOWN, UP, FLOOR, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN); // Exponents to test for the pow() function. static final ImmutableList<Integer> EXPONENTS = ImmutableList.of(0, 1, 2, 3, 4, 7, 10, 15, 20, 25, 40, 70); /* Helper function to make a Long value from an Integer. */ private static final Function<Integer, Long> TO_LONG = new Function<Integer, Long>() { @Override public Long apply(Integer n) { return Long.valueOf(n); } }; /* Helper function to make a BigInteger value from a Long. */ private static final Function<Long, BigInteger> TO_BIGINTEGER = new Function<Long, BigInteger>() { @Override public BigInteger apply(Long n) { return BigInteger.valueOf(n); } }; private static final Function<Integer, Integer> NEGATE_INT = new Function<Integer, Integer>() { @Override public Integer apply(Integer x) { return -x; } }; private static final Function<Long, Long> NEGATE_LONG = new Function<Long, Long>() { @Override public Long apply(Long x) { return -x; } }; private static final Function<BigInteger, BigInteger> NEGATE_BIGINT = new Function<BigInteger, BigInteger>() { @Override public BigInteger apply(BigInteger x) { return x.negate(); } }; /* * This list contains values that attempt to provoke overflow in integer operations. It contains * positive values on or near 2^N for N near multiples of 8 (near byte boundaries). */ static final ImmutableSet<Integer> POSITIVE_INTEGER_CANDIDATES; static final Iterable<Integer> NEGATIVE_INTEGER_CANDIDATES; static final Iterable<Integer> NONZERO_INTEGER_CANDIDATES; static final Iterable<Integer> ALL_INTEGER_CANDIDATES; static { ImmutableSet.Builder<Integer> intValues = ImmutableSet.builder(); // Add boundary values manually to avoid over/under flow (this covers 2^N for 0 and 31). intValues.add(Integer.MAX_VALUE - 1, Integer.MAX_VALUE); // Add values up to 40. This covers cases like "square of a prime" and such. for (int i = 1; i <= 40; i++) { intValues.add(i); } // Now add values near 2^N for lots of values of N. for (int exponent : asList(2, 3, 4, 9, 15, 16, 17, 24, 25, 30)) { int x = 1 << exponent; intValues.add(x, x + 1, x - 1); } intValues.add(9999).add(10000).add(10001).add(1000000); // near powers of 10 intValues.add(5792).add(5793); // sqrt(2^25) rounded up and down POSITIVE_INTEGER_CANDIDATES = intValues.build(); NEGATIVE_INTEGER_CANDIDATES = ImmutableList.copyOf( Iterables.concat( Iterables.transform(POSITIVE_INTEGER_CANDIDATES, NEGATE_INT), ImmutableList.of(Integer.MIN_VALUE))); NONZERO_INTEGER_CANDIDATES = ImmutableList.copyOf( Iterables.concat(POSITIVE_INTEGER_CANDIDATES, NEGATIVE_INTEGER_CANDIDATES)); ALL_INTEGER_CANDIDATES = Iterables.concat(NONZERO_INTEGER_CANDIDATES, ImmutableList.of(0)); } /* * This list contains values that attempt to provoke overflow in long operations. It contains * positive values on or near 2^N for N near multiples of 8 (near byte boundaries). This list is * a superset of POSITIVE_INTEGER_CANDIDATES. */ static final ImmutableSet<Long> POSITIVE_LONG_CANDIDATES; static final Iterable<Long> NEGATIVE_LONG_CANDIDATES; static final Iterable<Long> NONZERO_LONG_CANDIDATES; static final Iterable<Long> ALL_LONG_CANDIDATES; static { ImmutableSet.Builder<Long> longValues = ImmutableSet.builder(); // First of all add all the integer candidate values. longValues.addAll(Iterables.transform(POSITIVE_INTEGER_CANDIDATES, TO_LONG)); // Add boundary values manually to avoid over/under flow (this covers 2^N for 31 and 63). longValues.add(Integer.MAX_VALUE + 1L, Long.MAX_VALUE - 1L, Long.MAX_VALUE); // Now add values near 2^N for lots of values of N. for (int exponent : asList(32, 33, 39, 40, 41, 47, 48, 49, 55, 56, 57)) { long x = 1L << exponent; longValues.add(x, x + 1, x - 1); } longValues.add(194368031998L).add(194368031999L); // sqrt(2^75) rounded up and down POSITIVE_LONG_CANDIDATES = longValues.build(); NEGATIVE_LONG_CANDIDATES = Iterables.concat( Iterables.transform(POSITIVE_LONG_CANDIDATES, NEGATE_LONG), ImmutableList.of(Long.MIN_VALUE)); NONZERO_LONG_CANDIDATES = Iterables.concat(POSITIVE_LONG_CANDIDATES, NEGATIVE_LONG_CANDIDATES); ALL_LONG_CANDIDATES = Iterables.concat(NONZERO_LONG_CANDIDATES, ImmutableList.of(0L)); } /* * This list contains values that attempt to provoke overflow in big integer operations. It * contains positive values on or near 2^N for N near multiples of 8 (near byte boundaries). This * list is a superset of POSITIVE_LONG_CANDIDATES. */ static final ImmutableSet<BigInteger> POSITIVE_BIGINTEGER_CANDIDATES; static final Iterable<BigInteger> NEGATIVE_BIGINTEGER_CANDIDATES; static final Iterable<BigInteger> NONZERO_BIGINTEGER_CANDIDATES; static final Iterable<BigInteger> ALL_BIGINTEGER_CANDIDATES; static { ImmutableSet.Builder<BigInteger> bigValues = ImmutableSet.builder(); // First of all add all the long candidate values. bigValues.addAll(Iterables.transform(POSITIVE_LONG_CANDIDATES, TO_BIGINTEGER)); // Add boundary values manually to avoid over/under flow. bigValues.add(BigInteger.valueOf(Long.MAX_VALUE).add(ONE)); // Now add values near 2^N for lots of values of N. for (int exponent : asList( 64, 65, 71, 72, 73, 79, 80, 81, 255, 256, 257, 511, 512, 513, Double.MAX_EXPONENT - 1, Double.MAX_EXPONENT, Double.MAX_EXPONENT + 1)) { BigInteger x = ONE.shiftLeft(exponent); bigValues.add(x, x.add(ONE), x.subtract(ONE)); } bigValues.add(new BigInteger("218838949120258359057546633")); // sqrt(2^175) rounded up and // down bigValues.add(new BigInteger("218838949120258359057546634")); POSITIVE_BIGINTEGER_CANDIDATES = bigValues.build(); NEGATIVE_BIGINTEGER_CANDIDATES = Iterables.transform(POSITIVE_BIGINTEGER_CANDIDATES, NEGATE_BIGINT); NONZERO_BIGINTEGER_CANDIDATES = Iterables.concat(POSITIVE_BIGINTEGER_CANDIDATES, NEGATIVE_BIGINTEGER_CANDIDATES); ALL_BIGINTEGER_CANDIDATES = Iterables.concat(NONZERO_BIGINTEGER_CANDIDATES, ImmutableList.of(ZERO)); } static final ImmutableSet<Double> INTEGRAL_DOUBLE_CANDIDATES; static final ImmutableSet<Double> FRACTIONAL_DOUBLE_CANDIDATES; static final Iterable<Double> INFINITIES = Doubles.asList(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY); static final Iterable<Double> FINITE_DOUBLE_CANDIDATES; static final Iterable<Double> POSITIVE_FINITE_DOUBLE_CANDIDATES; static final Iterable<Double> ALL_DOUBLE_CANDIDATES; static final Iterable<Double> DOUBLE_CANDIDATES_EXCEPT_NAN; static { ImmutableSet.Builder<Double> integralBuilder = ImmutableSet.builder(); ImmutableSet.Builder<Double> fractionalBuilder = ImmutableSet.builder(); integralBuilder.addAll(Doubles.asList(0.0, -0.0, Double.MAX_VALUE, -Double.MAX_VALUE)); // Add small multiples of MIN_VALUE and MIN_NORMAL for (int scale = 1; scale <= 4; scale++) { for (double d : Doubles.asList(Double.MIN_VALUE, Double.MIN_NORMAL)) { fractionalBuilder.add(d * scale).add(-d * scale); } } for (double d : Doubles.asList( 0, 1, 2, 7, 51, 102, Math.scalb(1.0, 53), Integer.MIN_VALUE, Integer.MAX_VALUE, Long.MIN_VALUE, Long.MAX_VALUE)) { for (double delta : Doubles.asList(0.0, 1.0, 2.0)) { integralBuilder.addAll(Doubles.asList(d + delta, d - delta, -d - delta, -d + delta)); } for (double delta : Doubles.asList(0.01, 0.1, 0.25, 0.499, 0.5, 0.501, 0.7, 0.8)) { double x = d + delta; if (x != Math.round(x)) { fractionalBuilder.add(x); } } } INTEGRAL_DOUBLE_CANDIDATES = integralBuilder.build(); fractionalBuilder.add(1.414).add(1.415).add(Math.sqrt(2)); fractionalBuilder.add(5.656).add(5.657).add(4 * Math.sqrt(2)); for (double d : INTEGRAL_DOUBLE_CANDIDATES) { double x = 1 / d; if (x != Math.rint(x)) { fractionalBuilder.add(x); } } FRACTIONAL_DOUBLE_CANDIDATES = fractionalBuilder.build(); FINITE_DOUBLE_CANDIDATES = Iterables.concat(FRACTIONAL_DOUBLE_CANDIDATES, INTEGRAL_DOUBLE_CANDIDATES); POSITIVE_FINITE_DOUBLE_CANDIDATES = Iterables.filter( FINITE_DOUBLE_CANDIDATES, new Predicate<Double>() { @Override public boolean apply(Double input) { return input.doubleValue() > 0.0; } }); DOUBLE_CANDIDATES_EXCEPT_NAN = Iterables.concat(FINITE_DOUBLE_CANDIDATES, INFINITIES); ALL_DOUBLE_CANDIDATES = Iterables.concat(DOUBLE_CANDIDATES_EXCEPT_NAN, asList(Double.NaN)); } }
/** * Tests for {@code DoubleMath}. * * @author Louis Wasserman */ public class DoubleMathTest extends TestCase { private static final BigDecimal MAX_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MAX_VALUE); private static final BigDecimal MIN_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MIN_VALUE); private static final BigDecimal MAX_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE); private static final BigDecimal MIN_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE); public void testConstantsMaxFactorial() { BigInteger MAX_DOUBLE_VALUE = BigDecimal.valueOf(Double.MAX_VALUE).toBigInteger(); assertTrue(BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL).compareTo(MAX_DOUBLE_VALUE) <= 0); assertTrue( BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL + 1).compareTo(MAX_DOUBLE_VALUE) > 0); } public void testConstantsEverySixteenthFactorial() { for (int i = 0, n = 0; n <= DoubleMath.MAX_FACTORIAL; i++, n += 16) { assertEquals( BigIntegerMath.factorial(n).doubleValue(), DoubleMath.EVERY_SIXTEENTH_FACTORIAL[i]); } } public void testRoundIntegralDoubleToInt() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } } public void testRoundFractionalDoubleToInt() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } } public void testRoundExactIntegralDoubleToInt() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY); boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.intValue(), DoubleMath.roundToInt(d, UNNECESSARY)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } public void testRoundExactFractionalDoubleToIntFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { try { DoubleMath.roundToInt(d, UNNECESSARY); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundNaNToIntAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToInt(Double.NaN, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundInfiniteToIntAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToInt(Double.POSITIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } try { DoubleMath.roundToInt(Double.NEGATIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundIntegralDoubleToLong() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } } public void testRoundFractionalDoubleToLong() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } } public void testRoundExactIntegralDoubleToLong() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { // every mode except UNNECESSARY BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY); boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0 & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0; try { assertEquals(expected.longValue(), DoubleMath.roundToLong(d, UNNECESSARY)); assertTrue(isInBounds); } catch (ArithmeticException e) { assertFalse(isInBounds); } } } public void testRoundExactFractionalDoubleToLongFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { try { DoubleMath.roundToLong(d, UNNECESSARY); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundNaNToLongAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToLong(Double.NaN, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundInfiniteToLongAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToLong(Double.POSITIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } try { DoubleMath.roundToLong(Double.NEGATIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundIntegralDoubleToBigInteger() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode)); } } } public void testRoundFractionalDoubleToBigInteger() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) { BigDecimal expected = new BigDecimal(d).setScale(0, mode); assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode)); } } } public void testRoundExactIntegralDoubleToBigInteger() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY); assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, UNNECESSARY)); } } public void testRoundExactFractionalDoubleToBigIntegerFails() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { try { DoubleMath.roundToBigInteger(d, UNNECESSARY); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundNaNToBigIntegerAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToBigInteger(Double.NaN, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundInfiniteToBigIntegerAlwaysFails() { for (RoundingMode mode : ALL_ROUNDING_MODES) { try { DoubleMath.roundToBigInteger(Double.POSITIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } try { DoubleMath.roundToBigInteger(Double.NEGATIVE_INFINITY, mode); fail("Expected ArithmeticException"); } catch (ArithmeticException expected) { } } } public void testRoundLog2Floor() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { int log2 = DoubleMath.log2(d, FLOOR); assertTrue(StrictMath.pow(2.0, log2) <= d); assertTrue(StrictMath.pow(2.0, log2 + 1) > d); } } public void testRoundLog2Ceiling() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { int log2 = DoubleMath.log2(d, CEILING); assertTrue(StrictMath.pow(2.0, log2) >= d); double z = StrictMath.pow(2.0, log2 - 1); assertTrue(z < d); } } public void testRoundLog2Down() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { int log2 = DoubleMath.log2(d, DOWN); if (d >= 1.0) { assertTrue(log2 >= 0); assertTrue(StrictMath.pow(2.0, log2) <= d); assertTrue(StrictMath.pow(2.0, log2 + 1) > d); } else { assertTrue(log2 <= 0); assertTrue(StrictMath.pow(2.0, log2) >= d); assertTrue(StrictMath.pow(2.0, log2 - 1) < d); } } } public void testRoundLog2Up() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { int log2 = DoubleMath.log2(d, UP); if (d >= 1.0) { assertTrue(log2 >= 0); assertTrue(StrictMath.pow(2.0, log2) >= d); assertTrue(StrictMath.pow(2.0, log2 - 1) < d); } else { assertTrue(log2 <= 0); assertTrue(StrictMath.pow(2.0, log2) <= d); assertTrue(StrictMath.pow(2.0, log2 + 1) > d); } } } public void testRoundLog2Half() { // We don't expect perfect rounding accuracy. for (int exp : asList(-1022, -50, -1, 0, 1, 2, 3, 4, 100, 1022, 1023)) { for (RoundingMode mode : asList(HALF_EVEN, HALF_UP, HALF_DOWN)) { double x = Math.scalb(Math.sqrt(2) + 0.001, exp); double y = Math.scalb(Math.sqrt(2) - 0.001, exp); if (exp < 0) { assertEquals(exp + 1, DoubleMath.log2(x, mode)); assertEquals(exp, DoubleMath.log2(y, mode)); } else { assertEquals(exp + 1, DoubleMath.log2(x, mode)); assertEquals(exp, DoubleMath.log2(y, mode)); } } } } public void testRoundLog2Exact() { for (double x : POSITIVE_FINITE_DOUBLE_CANDIDATES) { boolean isPowerOfTwo = StrictMath.pow(2.0, DoubleMath.log2(x, FLOOR)) == x; try { int log2 = DoubleMath.log2(x, UNNECESSARY); assertEquals(x, Math.scalb(1.0, log2)); assertTrue(isPowerOfTwo); } catch (ArithmeticException e) { assertFalse(isPowerOfTwo); } } } public void testRoundLog2ThrowsOnZerosInfinitiesAndNaN() { for (RoundingMode mode : ALL_ROUNDING_MODES) { for (double d : asList(0.0, -0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) { try { DoubleMath.log2(d, mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } } } } public void testRoundLog2ThrowsOnNegative() { for (RoundingMode mode : ALL_ROUNDING_MODES) { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { try { DoubleMath.log2(-d, mode); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { } } } } public void testIsPowerOfTwoYes() { for (int i = -1074; i <= 1023; i++) { assertTrue(DoubleMath.isPowerOfTwo(StrictMath.pow(2.0, i))); } } public void testIsPowerOfTwo() { for (double x : ALL_DOUBLE_CANDIDATES) { boolean expected = x > 0 && !Double.isInfinite(x) && !Double.isNaN(x) && StrictMath.pow(2.0, DoubleMath.log2(x, FLOOR)) == x; assertEquals(expected, DoubleMath.isPowerOfTwo(x)); } } public void testLog2Accuracy() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { double dmLog2 = DoubleMath.log2(d); double trueLog2 = trueLog2(d); assertTrue(Math.abs(dmLog2 - trueLog2) <= Math.ulp(trueLog2)); } } public void testLog2SemiMonotonic() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { assertTrue(DoubleMath.log2(d + 0.01) >= DoubleMath.log2(d)); } } public void testLog2Negative() { for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) { assertTrue(Double.isNaN(DoubleMath.log2(-d))); } } public void testLog2Zero() { assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(0.0)); assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(-0.0)); } public void testLog2NaNInfinity() { assertEquals(Double.POSITIVE_INFINITY, DoubleMath.log2(Double.POSITIVE_INFINITY)); assertTrue(Double.isNaN(DoubleMath.log2(Double.NEGATIVE_INFINITY))); assertTrue(Double.isNaN(DoubleMath.log2(Double.NaN))); } private strictfp double trueLog2(double d) { double trueLog2 = StrictMath.log(d) / StrictMath.log(2); // increment until it's >= the true value while (StrictMath.pow(2.0, trueLog2) < d) { trueLog2 = StrictMath.nextUp(trueLog2); } // decrement until it's <= the true value while (StrictMath.pow(2.0, trueLog2) > d) { trueLog2 = StrictMath.nextAfter(trueLog2, Double.NEGATIVE_INFINITY); } if (StrictMath.abs(StrictMath.pow(2.0, trueLog2) - d) > StrictMath.abs(StrictMath.pow(2.0, StrictMath.nextUp(trueLog2)) - d)) { trueLog2 = StrictMath.nextUp(trueLog2); } return trueLog2; } public void testIsMathematicalIntegerIntegral() { for (double d : INTEGRAL_DOUBLE_CANDIDATES) { assertTrue(DoubleMath.isMathematicalInteger(d)); } } public void testIsMathematicalIntegerFractional() { for (double d : FRACTIONAL_DOUBLE_CANDIDATES) { assertFalse(DoubleMath.isMathematicalInteger(d)); } } public void testIsMathematicalIntegerNotFinite() { for (double d : Arrays.asList(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) { assertFalse(DoubleMath.isMathematicalInteger(d)); } } public void testFactorial() { for (int i = 0; i <= DoubleMath.MAX_FACTORIAL; i++) { double actual = BigIntegerMath.factorial(i).doubleValue(); double result = DoubleMath.factorial(i); assertEquals(actual, result, Math.ulp(actual)); } } public void testFactorialTooHigh() { assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 1)); assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 20)); } public void testFactorialNegative() { for (int n : NEGATIVE_INTEGER_CANDIDATES) { try { DoubleMath.factorial(n); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } } private static final ImmutableList<Double> FINITE_TOLERANCE_CANDIDATES = ImmutableList.of(-0.0, 0.0, 1.0, 100.0, 10000.0, Double.MAX_VALUE); private static final Iterable<Double> TOLERANCE_CANDIDATES = Iterables.concat(FINITE_TOLERANCE_CANDIDATES, ImmutableList.of(Double.POSITIVE_INFINITY)); private static final List<Double> BAD_TOLERANCE_CANDIDATES = Doubles.asList( -Double.MIN_VALUE, -Double.MIN_NORMAL, -1, -20, Double.NaN, Double.NEGATIVE_INFINITY, -0.001); public void testFuzzyEqualsFinite() { for (double a : FINITE_DOUBLE_CANDIDATES) { for (double b : FINITE_DOUBLE_CANDIDATES) { for (double tolerance : FINITE_TOLERANCE_CANDIDATES) { assertEquals(Math.abs(a - b) <= tolerance, DoubleMath.fuzzyEquals(a, b, tolerance)); } } } } public void testFuzzyInfiniteVersusFiniteWithFiniteTolerance() { for (double inf : INFINITIES) { for (double a : FINITE_DOUBLE_CANDIDATES) { for (double tolerance : FINITE_TOLERANCE_CANDIDATES) { assertFalse(DoubleMath.fuzzyEquals(a, inf, tolerance)); assertFalse(DoubleMath.fuzzyEquals(inf, a, tolerance)); } } } } public void testFuzzyInfiniteVersusInfiniteWithFiniteTolerance() { for (double inf : INFINITIES) { for (double tolerance : FINITE_TOLERANCE_CANDIDATES) { assertTrue(DoubleMath.fuzzyEquals(inf, inf, tolerance)); assertFalse(DoubleMath.fuzzyEquals(inf, -inf, tolerance)); } } } public void testFuzzyEqualsInfiniteTolerance() { for (double a : DOUBLE_CANDIDATES_EXCEPT_NAN) { for (double b : DOUBLE_CANDIDATES_EXCEPT_NAN) { assertTrue(DoubleMath.fuzzyEquals(a, b, Double.POSITIVE_INFINITY)); } } } public void testFuzzyEqualsOneNaN() { for (double a : DOUBLE_CANDIDATES_EXCEPT_NAN) { for (double tolerance : TOLERANCE_CANDIDATES) { assertFalse(DoubleMath.fuzzyEquals(a, Double.NaN, tolerance)); assertFalse(DoubleMath.fuzzyEquals(Double.NaN, a, tolerance)); } } } public void testFuzzyEqualsTwoNaNs() { for (double tolerance : TOLERANCE_CANDIDATES) { assertTrue(DoubleMath.fuzzyEquals(Double.NaN, Double.NaN, tolerance)); } } public void testFuzzyEqualsZeroTolerance() { // make sure we test -0 tolerance for (double zero : Doubles.asList(0.0, -0.0)) { for (double a : ALL_DOUBLE_CANDIDATES) { for (double b : ALL_DOUBLE_CANDIDATES) { assertEquals( a == b || (Double.isNaN(a) && Double.isNaN(b)), DoubleMath.fuzzyEquals(a, b, zero)); } } } } public void testFuzzyEqualsBadTolerance() { for (double tolerance : BAD_TOLERANCE_CANDIDATES) { try { DoubleMath.fuzzyEquals(1, 2, tolerance); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // success } } } public void testFuzzyCompare() { for (double a : ALL_DOUBLE_CANDIDATES) { for (double b : ALL_DOUBLE_CANDIDATES) { for (double tolerance : TOLERANCE_CANDIDATES) { int expected = DoubleMath.fuzzyEquals(a, b, tolerance) ? 0 : Double.compare(a, b); int actual = DoubleMath.fuzzyCompare(a, b, tolerance); assertEquals(Integer.signum(expected), Integer.signum(actual)); } } } } public void testFuzzyCompareBadTolerance() { for (double tolerance : BAD_TOLERANCE_CANDIDATES) { try { DoubleMath.fuzzyCompare(1, 2, tolerance); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { // success } } } public void testNullPointers() { NullPointerTester tester = new NullPointerTester(); tester.setDefault(RoundingMode.class, FLOOR); tester.setDefault(double.class, 3.0); tester.testAllPublicStaticMethods(DoubleMath.class); } }
private static ImmutableList<Double> values(double... values) { return ImmutableList.copyOf(Doubles.asList(values)); }