DoubleMathTest.java revision 7dd252788645e940eada959bdde927426e2531c9
1/*
2 * Copyright (C) 2011 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.math;
18
19import static com.google.common.math.MathTesting.ALL_DOUBLE_CANDIDATES;
20import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES;
21import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES;
22import static com.google.common.math.MathTesting.DOUBLE_CANDIDATES_EXCEPT_NAN;
23import static com.google.common.math.MathTesting.FINITE_DOUBLE_CANDIDATES;
24import static com.google.common.math.MathTesting.FRACTIONAL_DOUBLE_CANDIDATES;
25import static com.google.common.math.MathTesting.INFINITIES;
26import static com.google.common.math.MathTesting.INTEGRAL_DOUBLE_CANDIDATES;
27import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
28import static com.google.common.math.MathTesting.POSITIVE_FINITE_DOUBLE_CANDIDATES;
29import static java.math.RoundingMode.CEILING;
30import static java.math.RoundingMode.DOWN;
31import static java.math.RoundingMode.FLOOR;
32import static java.math.RoundingMode.UNNECESSARY;
33import static java.math.RoundingMode.UP;
34import static java.util.Arrays.asList;
35
36import com.google.common.collect.ImmutableList;
37import com.google.common.collect.Iterables;
38import com.google.common.primitives.Doubles;
39import com.google.common.testing.NullPointerTester;
40
41import junit.framework.TestCase;
42
43import java.math.BigDecimal;
44import java.math.BigInteger;
45import java.math.RoundingMode;
46import java.util.Arrays;
47import java.util.List;
48
49/**
50 * Tests for {@code DoubleMath}.
51 *
52 * @author Louis Wasserman
53 */
54public class DoubleMathTest extends TestCase {
55
56  private static final BigDecimal MAX_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MAX_VALUE);
57  private static final BigDecimal MIN_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MIN_VALUE);
58
59  private static final BigDecimal MAX_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE);
60  private static final BigDecimal MIN_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE);
61
62  private static final double MIN_NORMAL = 2.2250738585072014E-308; // Doubles.MIN_NORMAL from 1.6
63
64  public void testConstantsMaxFactorial() {
65    BigInteger MAX_DOUBLE_VALUE = BigDecimal.valueOf(Double.MAX_VALUE).toBigInteger();
66    assertTrue(BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL).compareTo(MAX_DOUBLE_VALUE) <= 0);
67    assertTrue(
68        BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL + 1).compareTo(MAX_DOUBLE_VALUE) > 0);
69  }
70
71  public void testConstantsEverySixteenthFactorial() {
72    for (int i = 0, n = 0; n <= DoubleMath.MAX_FACTORIAL; i++, n += 16) {
73      assertEquals(
74          BigIntegerMath.factorial(n).doubleValue(), DoubleMath.everySixteenthFactorial[i]);
75    }
76  }
77
78  public void testRoundIntegralDoubleToInt() {
79    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
80      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
81        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
82        boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
83            & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
84
85        try {
86          assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode));
87          assertTrue(isInBounds);
88        } catch (ArithmeticException e) {
89          assertFalse(isInBounds);
90        }
91      }
92    }
93  }
94
95  public void testRoundFractionalDoubleToInt() {
96    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
97      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
98        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
99        boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
100            & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
101
102        try {
103          assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode));
104          assertTrue(isInBounds);
105        } catch (ArithmeticException e) {
106          assertFalse(isInBounds);
107        }
108      }
109    }
110  }
111
112  public void testRoundExactIntegralDoubleToInt() {
113    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
114      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
115      boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
116          & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
117
118      try {
119        assertEquals(expected.intValue(), DoubleMath.roundToInt(d, UNNECESSARY));
120        assertTrue(isInBounds);
121      } catch (ArithmeticException e) {
122        assertFalse(isInBounds);
123      }
124    }
125  }
126
127  public void testRoundExactFractionalDoubleToIntFails() {
128    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
129      try {
130        DoubleMath.roundToInt(d, UNNECESSARY);
131        fail("Expected ArithmeticException");
132      } catch (ArithmeticException expected) {}
133    }
134  }
135
136  public void testRoundNaNToIntAlwaysFails() {
137    for (RoundingMode mode : ALL_ROUNDING_MODES) {
138      try {
139        DoubleMath.roundToInt(Double.NaN, mode);
140        fail("Expected ArithmeticException");
141      } catch (ArithmeticException expected) {}
142    }
143  }
144
145  public void testRoundInfiniteToIntAlwaysFails() {
146    for (RoundingMode mode : ALL_ROUNDING_MODES) {
147      try {
148        DoubleMath.roundToInt(Double.POSITIVE_INFINITY, mode);
149        fail("Expected ArithmeticException");
150      } catch (ArithmeticException expected) {}
151      try {
152        DoubleMath.roundToInt(Double.NEGATIVE_INFINITY, mode);
153        fail("Expected ArithmeticException");
154      } catch (ArithmeticException expected) {}
155    }
156  }
157
158  public void testRoundIntegralDoubleToLong() {
159    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
160      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
161        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
162        boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
163            & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
164
165        try {
166          assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode));
167          assertTrue(isInBounds);
168        } catch (ArithmeticException e) {
169          assertFalse(isInBounds);
170        }
171      }
172    }
173  }
174
175  public void testRoundFractionalDoubleToLong() {
176    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
177      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
178        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
179        boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
180            & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
181
182        try {
183          assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode));
184          assertTrue(isInBounds);
185        } catch (ArithmeticException e) {
186          assertFalse(isInBounds);
187        }
188      }
189    }
190  }
191
192  public void testRoundExactIntegralDoubleToLong() {
193    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
194      // every mode except UNNECESSARY
195      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
196      boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
197          & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
198
199      try {
200        assertEquals(expected.longValue(), DoubleMath.roundToLong(d, UNNECESSARY));
201        assertTrue(isInBounds);
202      } catch (ArithmeticException e) {
203        assertFalse(isInBounds);
204      }
205    }
206  }
207
208  public void testRoundExactFractionalDoubleToLongFails() {
209    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
210      try {
211        DoubleMath.roundToLong(d, UNNECESSARY);
212        fail("Expected ArithmeticException");
213      } catch (ArithmeticException expected) {}
214    }
215  }
216
217  public void testRoundNaNToLongAlwaysFails() {
218    for (RoundingMode mode : ALL_ROUNDING_MODES) {
219      try {
220        DoubleMath.roundToLong(Double.NaN, mode);
221        fail("Expected ArithmeticException");
222      } catch (ArithmeticException expected) {}
223    }
224  }
225
226  public void testRoundInfiniteToLongAlwaysFails() {
227    for (RoundingMode mode : ALL_ROUNDING_MODES) {
228      try {
229        DoubleMath.roundToLong(Double.POSITIVE_INFINITY, mode);
230        fail("Expected ArithmeticException");
231      } catch (ArithmeticException expected) {}
232      try {
233        DoubleMath.roundToLong(Double.NEGATIVE_INFINITY, mode);
234        fail("Expected ArithmeticException");
235      } catch (ArithmeticException expected) {}
236    }
237  }
238
239  public void testRoundIntegralDoubleToBigInteger() {
240    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
241      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
242        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
243        assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode));
244      }
245    }
246  }
247
248  public void testRoundFractionalDoubleToBigInteger() {
249    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
250      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
251        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
252        assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode));
253      }
254    }
255  }
256
257  public void testRoundExactIntegralDoubleToBigInteger() {
258    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
259      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
260      assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, UNNECESSARY));
261    }
262  }
263
264  public void testRoundExactFractionalDoubleToBigIntegerFails() {
265    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
266      try {
267        DoubleMath.roundToBigInteger(d, UNNECESSARY);
268        fail("Expected ArithmeticException");
269      } catch (ArithmeticException expected) {}
270    }
271  }
272
273  public void testRoundNaNToBigIntegerAlwaysFails() {
274    for (RoundingMode mode : ALL_ROUNDING_MODES) {
275      try {
276        DoubleMath.roundToBigInteger(Double.NaN, mode);
277        fail("Expected ArithmeticException");
278      } catch (ArithmeticException expected) {}
279    }
280  }
281
282  public void testRoundInfiniteToBigIntegerAlwaysFails() {
283    for (RoundingMode mode : ALL_ROUNDING_MODES) {
284      try {
285        DoubleMath.roundToBigInteger(Double.POSITIVE_INFINITY, mode);
286        fail("Expected ArithmeticException");
287      } catch (ArithmeticException expected) {}
288      try {
289        DoubleMath.roundToBigInteger(Double.NEGATIVE_INFINITY, mode);
290        fail("Expected ArithmeticException");
291      } catch (ArithmeticException expected) {}
292    }
293  }
294
295  public void testRoundLog2Floor() {
296    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
297      int log2 = DoubleMath.log2(d, FLOOR);
298      assertTrue(StrictMath.pow(2.0, log2) <= d);
299      assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
300    }
301  }
302
303  public void testRoundLog2Ceiling() {
304    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
305      int log2 = DoubleMath.log2(d, CEILING);
306      assertTrue(StrictMath.pow(2.0, log2) >= d);
307      double z = StrictMath.pow(2.0, log2 - 1);
308      assertTrue(z < d);
309    }
310  }
311
312  public void testRoundLog2Down() {
313    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
314      int log2 = DoubleMath.log2(d, DOWN);
315      if (d >= 1.0) {
316        assertTrue(log2 >= 0);
317        assertTrue(StrictMath.pow(2.0, log2) <= d);
318        assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
319      } else {
320        assertTrue(log2 <= 0);
321        assertTrue(StrictMath.pow(2.0, log2) >= d);
322        assertTrue(StrictMath.pow(2.0, log2 - 1) < d);
323      }
324    }
325  }
326
327  public void testRoundLog2Up() {
328    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
329      int log2 = DoubleMath.log2(d, UP);
330      if (d >= 1.0) {
331        assertTrue(log2 >= 0);
332        assertTrue(StrictMath.pow(2.0, log2) >= d);
333        assertTrue(StrictMath.pow(2.0, log2 - 1) < d);
334      } else {
335        assertTrue(log2 <= 0);
336        assertTrue(StrictMath.pow(2.0, log2) <= d);
337        assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
338      }
339    }
340  }
341
342
343  public void testRoundLog2ThrowsOnZerosInfinitiesAndNaN() {
344    for (RoundingMode mode : ALL_ROUNDING_MODES) {
345      for (double d :
346          asList(0.0, -0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) {
347        try {
348          DoubleMath.log2(d, mode);
349          fail("Expected IllegalArgumentException");
350        } catch (IllegalArgumentException e) {}
351      }
352    }
353  }
354
355  public void testRoundLog2ThrowsOnNegative() {
356    for (RoundingMode mode : ALL_ROUNDING_MODES) {
357      for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
358        try {
359          DoubleMath.log2(-d, mode);
360          fail("Expected IllegalArgumentException");
361        } catch (IllegalArgumentException e) {}
362      }
363    }
364  }
365
366  public void testIsPowerOfTwoYes() {
367    for (int i = -1074; i <= 1023; i++) {
368      assertTrue(DoubleMath.isPowerOfTwo(StrictMath.pow(2.0, i)));
369    }
370  }
371
372  public void testIsPowerOfTwo() {
373    for (double x : ALL_DOUBLE_CANDIDATES) {
374      boolean expected = x > 0 && !Double.isInfinite(x) && !Double.isNaN(x)
375          && StrictMath.pow(2.0, DoubleMath.log2(x, FLOOR)) == x;
376      assertEquals(expected, DoubleMath.isPowerOfTwo(x));
377    }
378  }
379
380
381  public void testLog2SemiMonotonic() {
382    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
383      assertTrue(DoubleMath.log2(d + 0.01) >= DoubleMath.log2(d));
384    }
385  }
386
387  public void testLog2Negative() {
388    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
389      assertTrue(Double.isNaN(DoubleMath.log2(-d)));
390    }
391  }
392
393  public void testLog2Zero() {
394    assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(0.0));
395    assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(-0.0));
396  }
397
398  public void testLog2NaNInfinity() {
399    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.log2(Double.POSITIVE_INFINITY));
400    assertTrue(Double.isNaN(DoubleMath.log2(Double.NEGATIVE_INFINITY)));
401    assertTrue(Double.isNaN(DoubleMath.log2(Double.NaN)));
402  }
403
404  public void testIsMathematicalIntegerIntegral() {
405    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
406      assertTrue(DoubleMath.isMathematicalInteger(d));
407    }
408  }
409
410  public void testIsMathematicalIntegerFractional() {
411    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
412      assertFalse(DoubleMath.isMathematicalInteger(d));
413    }
414  }
415
416  public void testIsMathematicalIntegerNotFinite() {
417    for (double d :
418        Arrays.asList(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) {
419      assertFalse(DoubleMath.isMathematicalInteger(d));
420    }
421  }
422
423  public void testFactorial() {
424    for (int i = 0; i <= DoubleMath.MAX_FACTORIAL; i++) {
425      double actual = BigIntegerMath.factorial(i).doubleValue();
426      double result = DoubleMath.factorial(i);
427      assertEquals(actual, result, Math.ulp(actual));
428    }
429  }
430
431  public void testFactorialTooHigh() {
432    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 1));
433    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 20));
434  }
435
436  public void testFactorialNegative() {
437    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
438      try {
439        DoubleMath.factorial(n);
440        fail("Expected IllegalArgumentException");
441      } catch (IllegalArgumentException expected) {}
442    }
443  }
444
445  private static final ImmutableList<Double> FINITE_TOLERANCE_CANDIDATES =
446      ImmutableList.of(-0.0, 0.0, 1.0, 100.0, 10000.0, Double.MAX_VALUE);
447
448  private static final Iterable<Double> TOLERANCE_CANDIDATES =
449      Iterables.concat(FINITE_TOLERANCE_CANDIDATES, ImmutableList.of(Double.POSITIVE_INFINITY));
450
451  private static final List<Double> BAD_TOLERANCE_CANDIDATES =
452      Doubles.asList(-Double.MIN_VALUE, -MIN_NORMAL, -1, -20, Double.NaN,
453          Double.NEGATIVE_INFINITY, -0.001);
454
455  public void testFuzzyEqualsFinite() {
456    for (double a : FINITE_DOUBLE_CANDIDATES) {
457      for (double b : FINITE_DOUBLE_CANDIDATES) {
458        for (double tolerance : FINITE_TOLERANCE_CANDIDATES) {
459          assertEquals(
460              Math.abs(a - b) <= tolerance,
461              DoubleMath.fuzzyEquals(a, b, tolerance));
462        }
463      }
464    }
465  }
466
467  public void testFuzzyInfiniteVersusFiniteWithFiniteTolerance() {
468    for (double inf : INFINITIES) {
469      for (double a : FINITE_DOUBLE_CANDIDATES) {
470        for (double tolerance : FINITE_TOLERANCE_CANDIDATES) {
471          assertFalse(DoubleMath.fuzzyEquals(a, inf, tolerance));
472          assertFalse(DoubleMath.fuzzyEquals(inf, a, tolerance));
473        }
474      }
475    }
476  }
477
478  public void testFuzzyInfiniteVersusInfiniteWithFiniteTolerance() {
479    for (double inf : INFINITIES) {
480      for (double tolerance : FINITE_TOLERANCE_CANDIDATES) {
481        assertTrue(DoubleMath.fuzzyEquals(inf, inf, tolerance));
482        assertFalse(DoubleMath.fuzzyEquals(inf, -inf, tolerance));
483      }
484    }
485  }
486
487  public void testFuzzyEqualsInfiniteTolerance() {
488    for (double a : DOUBLE_CANDIDATES_EXCEPT_NAN) {
489      for (double b : DOUBLE_CANDIDATES_EXCEPT_NAN) {
490        assertTrue(DoubleMath.fuzzyEquals(a, b, Double.POSITIVE_INFINITY));
491      }
492    }
493  }
494
495  public void testFuzzyEqualsOneNaN() {
496    for (double a : DOUBLE_CANDIDATES_EXCEPT_NAN) {
497      for (double tolerance : TOLERANCE_CANDIDATES) {
498        assertFalse(DoubleMath.fuzzyEquals(a, Double.NaN, tolerance));
499        assertFalse(DoubleMath.fuzzyEquals(Double.NaN, a, tolerance));
500      }
501    }
502  }
503
504  public void testFuzzyEqualsTwoNaNs() {
505    for (double tolerance : TOLERANCE_CANDIDATES) {
506      assertTrue(DoubleMath.fuzzyEquals(Double.NaN, Double.NaN, tolerance));
507    }
508  }
509
510  public void testFuzzyEqualsZeroTolerance() {
511    // make sure we test -0 tolerance
512    for (double zero : Doubles.asList(0.0, -0.0)) {
513      for (double a : ALL_DOUBLE_CANDIDATES) {
514        for (double b : ALL_DOUBLE_CANDIDATES) {
515          assertEquals(a == b || (Double.isNaN(a) && Double.isNaN(b)),
516              DoubleMath.fuzzyEquals(a, b, zero));
517        }
518      }
519    }
520  }
521
522  public void testFuzzyEqualsBadTolerance() {
523    for (double tolerance : BAD_TOLERANCE_CANDIDATES) {
524      try {
525        DoubleMath.fuzzyEquals(1, 2, tolerance);
526        fail("Expected IllegalArgumentException");
527      } catch (IllegalArgumentException expected) {
528        // success
529      }
530    }
531  }
532
533  public void testFuzzyCompare() {
534    for (double a : ALL_DOUBLE_CANDIDATES) {
535      for (double b : ALL_DOUBLE_CANDIDATES) {
536        for (double tolerance : TOLERANCE_CANDIDATES) {
537          int expected = DoubleMath.fuzzyEquals(a, b, tolerance) ? 0 : Double.compare(a, b);
538          int actual = DoubleMath.fuzzyCompare(a, b, tolerance);
539          assertEquals(Integer.signum(expected), Integer.signum(actual));
540        }
541      }
542    }
543  }
544
545  public void testFuzzyCompareBadTolerance() {
546    for (double tolerance : BAD_TOLERANCE_CANDIDATES) {
547      try {
548        DoubleMath.fuzzyCompare(1, 2, tolerance);
549        fail("Expected IllegalArgumentException");
550      } catch (IllegalArgumentException expected) {
551        // success
552      }
553    }
554  }
555
556  public void testNullPointers() {
557    NullPointerTester tester = new NullPointerTester();
558    tester.setDefault(double.class, 3.0);
559    tester.testAllPublicStaticMethods(DoubleMath.class);
560  }
561}
562