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.FRACTIONAL_DOUBLE_CANDIDATES;
23import static com.google.common.math.MathTesting.INTEGRAL_DOUBLE_CANDIDATES;
24import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
25import static com.google.common.math.MathTesting.POSITIVE_FINITE_DOUBLE_CANDIDATES;
26import static java.math.RoundingMode.CEILING;
27import static java.math.RoundingMode.DOWN;
28import static java.math.RoundingMode.FLOOR;
29import static java.math.RoundingMode.HALF_DOWN;
30import static java.math.RoundingMode.HALF_EVEN;
31import static java.math.RoundingMode.HALF_UP;
32import static java.math.RoundingMode.UNNECESSARY;
33import static java.math.RoundingMode.UP;
34import static java.util.Arrays.asList;
35
36import com.google.common.testing.NullPointerTester;
37
38import junit.framework.TestCase;
39
40import java.math.BigDecimal;
41import java.math.BigInteger;
42import java.math.RoundingMode;
43import java.util.Arrays;
44
45/**
46 * Tests for {@code DoubleMath}.
47 *
48 * @author Louis Wasserman
49 */
50public class DoubleMathTest extends TestCase {
51
52  private static final BigDecimal MAX_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MAX_VALUE);
53  private static final BigDecimal MIN_INT_AS_BIG_DECIMAL = BigDecimal.valueOf(Integer.MIN_VALUE);
54
55  private static final BigDecimal MAX_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MAX_VALUE);
56  private static final BigDecimal MIN_LONG_AS_BIG_DECIMAL = BigDecimal.valueOf(Long.MIN_VALUE);
57
58  public void testConstantsMaxFactorial(){
59    BigInteger MAX_DOUBLE_VALUE = BigDecimal.valueOf(Double.MAX_VALUE).toBigInteger();
60    assertTrue(BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL).compareTo(MAX_DOUBLE_VALUE) <= 0);
61    assertTrue(
62        BigIntegerMath.factorial(DoubleMath.MAX_FACTORIAL + 1).compareTo(MAX_DOUBLE_VALUE) > 0);
63  }
64
65  public void testConstantsEverySixteenthFactorial() {
66    for (int i = 0, n = 0; n <= DoubleMath.MAX_FACTORIAL; i++, n += 16) {
67      assertEquals(
68          BigIntegerMath.factorial(n).doubleValue(), DoubleMath.EVERY_SIXTEENTH_FACTORIAL[i]);
69    }
70  }
71
72  public void testRoundIntegralDoubleToInt() {
73    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
74      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
75        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
76        boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
77            & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
78
79        try {
80          assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode));
81          assertTrue(isInBounds);
82        } catch (ArithmeticException e) {
83          assertFalse(isInBounds);
84        }
85      }
86    }
87  }
88
89  public void testRoundFractionalDoubleToInt() {
90    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
91      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
92        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
93        boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
94            & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
95
96        try {
97          assertEquals(expected.intValue(), DoubleMath.roundToInt(d, mode));
98          assertTrue(isInBounds);
99        } catch (ArithmeticException e) {
100          assertFalse(isInBounds);
101        }
102      }
103    }
104  }
105
106  public void testRoundExactIntegralDoubleToInt() {
107    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
108      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
109      boolean isInBounds = expected.compareTo(MAX_INT_AS_BIG_DECIMAL) <= 0
110          & expected.compareTo(MIN_INT_AS_BIG_DECIMAL) >= 0;
111
112      try {
113        assertEquals(expected.intValue(), DoubleMath.roundToInt(d, UNNECESSARY));
114        assertTrue(isInBounds);
115      } catch (ArithmeticException e) {
116        assertFalse(isInBounds);
117      }
118    }
119  }
120
121  public void testRoundExactFractionalDoubleToIntFails() {
122    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
123      try {
124        DoubleMath.roundToInt(d, UNNECESSARY);
125        fail("Expected ArithmeticException");
126      } catch (ArithmeticException expected) {}
127    }
128  }
129
130  public void testRoundNaNToIntAlwaysFails() {
131    for (RoundingMode mode : ALL_ROUNDING_MODES) {
132      try {
133        DoubleMath.roundToInt(Double.NaN, mode);
134        fail("Expected ArithmeticException");
135      } catch (ArithmeticException expected) {}
136    }
137  }
138
139  public void testRoundInfiniteToIntAlwaysFails() {
140    for (RoundingMode mode : ALL_ROUNDING_MODES) {
141      try {
142        DoubleMath.roundToInt(Double.POSITIVE_INFINITY, mode);
143        fail("Expected ArithmeticException");
144      } catch (ArithmeticException expected) {}
145      try {
146        DoubleMath.roundToInt(Double.NEGATIVE_INFINITY, mode);
147        fail("Expected ArithmeticException");
148      } catch (ArithmeticException expected) {}
149    }
150  }
151
152  public void testRoundIntegralDoubleToLong() {
153    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
154      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
155        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
156        boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
157            & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
158
159        try {
160          assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode));
161          assertTrue(isInBounds);
162        } catch (ArithmeticException e) {
163          assertFalse(isInBounds);
164        }
165      }
166    }
167  }
168
169  public void testRoundFractionalDoubleToLong() {
170    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
171      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
172        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
173        boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
174            & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
175
176        try {
177          assertEquals(expected.longValue(), DoubleMath.roundToLong(d, mode));
178          assertTrue(isInBounds);
179        } catch (ArithmeticException e) {
180          assertFalse(isInBounds);
181        }
182      }
183    }
184  }
185
186  public void testRoundExactIntegralDoubleToLong() {
187    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
188      // every mode except UNNECESSARY
189      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
190      boolean isInBounds = expected.compareTo(MAX_LONG_AS_BIG_DECIMAL) <= 0
191          & expected.compareTo(MIN_LONG_AS_BIG_DECIMAL) >= 0;
192
193      try {
194        assertEquals(expected.longValue(), DoubleMath.roundToLong(d, UNNECESSARY));
195        assertTrue(isInBounds);
196      } catch (ArithmeticException e) {
197        assertFalse(isInBounds);
198      }
199    }
200  }
201
202  public void testRoundExactFractionalDoubleToLongFails() {
203    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
204      try {
205        DoubleMath.roundToLong(d, UNNECESSARY);
206        fail("Expected ArithmeticException");
207      } catch (ArithmeticException expected) {}
208    }
209  }
210
211  public void testRoundNaNToLongAlwaysFails() {
212    for (RoundingMode mode : ALL_ROUNDING_MODES) {
213      try {
214        DoubleMath.roundToLong(Double.NaN, mode);
215        fail("Expected ArithmeticException");
216      } catch (ArithmeticException expected) {}
217    }
218  }
219
220  public void testRoundInfiniteToLongAlwaysFails() {
221    for (RoundingMode mode : ALL_ROUNDING_MODES) {
222      try {
223        DoubleMath.roundToLong(Double.POSITIVE_INFINITY, mode);
224        fail("Expected ArithmeticException");
225      } catch (ArithmeticException expected) {}
226      try {
227        DoubleMath.roundToLong(Double.NEGATIVE_INFINITY, mode);
228        fail("Expected ArithmeticException");
229      } catch (ArithmeticException expected) {}
230    }
231  }
232
233  public void testRoundIntegralDoubleToBigInteger() {
234    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
235      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
236        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
237        assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode));
238      }
239    }
240  }
241
242  public void testRoundFractionalDoubleToBigInteger() {
243    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
244      for (RoundingMode mode : ALL_SAFE_ROUNDING_MODES) {
245        BigDecimal expected = new BigDecimal(d).setScale(0, mode);
246        assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, mode));
247      }
248    }
249  }
250
251  public void testRoundExactIntegralDoubleToBigInteger() {
252    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
253      BigDecimal expected = new BigDecimal(d).setScale(0, UNNECESSARY);
254      assertEquals(expected.toBigInteger(), DoubleMath.roundToBigInteger(d, UNNECESSARY));
255    }
256  }
257
258  public void testRoundExactFractionalDoubleToBigIntegerFails() {
259    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
260      try {
261        DoubleMath.roundToBigInteger(d, UNNECESSARY);
262        fail("Expected ArithmeticException");
263      } catch (ArithmeticException expected) {}
264    }
265  }
266
267  public void testRoundNaNToBigIntegerAlwaysFails() {
268    for (RoundingMode mode : ALL_ROUNDING_MODES) {
269      try {
270        DoubleMath.roundToBigInteger(Double.NaN, mode);
271        fail("Expected ArithmeticException");
272      } catch (ArithmeticException expected) {}
273    }
274  }
275
276  public void testRoundInfiniteToBigIntegerAlwaysFails() {
277    for (RoundingMode mode : ALL_ROUNDING_MODES) {
278      try {
279        DoubleMath.roundToBigInteger(Double.POSITIVE_INFINITY, mode);
280        fail("Expected ArithmeticException");
281      } catch (ArithmeticException expected) {}
282      try {
283        DoubleMath.roundToBigInteger(Double.NEGATIVE_INFINITY, mode);
284        fail("Expected ArithmeticException");
285      } catch (ArithmeticException expected) {}
286    }
287  }
288
289  public void testRoundLog2Floor() {
290    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
291      int log2 = DoubleMath.log2(d, FLOOR);
292      assertTrue(StrictMath.pow(2.0, log2) <= d);
293      assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
294    }
295  }
296
297  public void testRoundLog2Ceiling() {
298    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
299      int log2 = DoubleMath.log2(d, CEILING);
300      assertTrue(StrictMath.pow(2.0, log2) >= d);
301      double z = StrictMath.pow(2.0, log2 - 1);
302      assertTrue(z < d);
303    }
304  }
305
306  public void testRoundLog2Down() {
307    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
308      int log2 = DoubleMath.log2(d, DOWN);
309      if (d >= 1.0) {
310        assertTrue(log2 >= 0);
311        assertTrue(StrictMath.pow(2.0, log2) <= d);
312        assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
313      } else {
314        assertTrue(log2 <= 0);
315        assertTrue(StrictMath.pow(2.0, log2) >= d);
316        assertTrue(StrictMath.pow(2.0, log2 - 1) < d);
317      }
318    }
319  }
320
321  public void testRoundLog2Up() {
322    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
323      int log2 = DoubleMath.log2(d, UP);
324      if (d >= 1.0) {
325        assertTrue(log2 >= 0);
326        assertTrue(StrictMath.pow(2.0, log2) >= d);
327        assertTrue(StrictMath.pow(2.0, log2 - 1) < d);
328      } else {
329        assertTrue(log2 <= 0);
330        assertTrue(StrictMath.pow(2.0, log2) <= d);
331        assertTrue(StrictMath.pow(2.0, log2 + 1) > d);
332      }
333    }
334  }
335
336  public void testRoundLog2Half() {
337    // We don't expect perfect rounding accuracy.
338    for (int exp : asList(-1022, -50, -1, 0, 1, 2, 3, 4, 100, 1022, 1023)) {
339      for (RoundingMode mode : asList(HALF_EVEN, HALF_UP, HALF_DOWN)) {
340        double x = Math.scalb(Math.sqrt(2) + 0.001, exp);
341        double y = Math.scalb(Math.sqrt(2) - 0.001, exp);
342        if (exp < 0) {
343          assertEquals(exp + 1, DoubleMath.log2(x, mode));
344          assertEquals(exp, DoubleMath.log2(y, mode));
345        } else {
346          assertEquals(exp + 1, DoubleMath.log2(x, mode));
347          assertEquals(exp, DoubleMath.log2(y, mode));
348        }
349      }
350    }
351  }
352
353  public void testRoundLog2ThrowsOnZerosInfinitiesAndNaN() {
354    for (RoundingMode mode : ALL_ROUNDING_MODES) {
355      for (double d :
356          asList(0.0, -0.0, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) {
357        try {
358          DoubleMath.log2(d, mode);
359          fail("Expected IllegalArgumentException");
360        } catch (IllegalArgumentException e) {}
361      }
362    }
363  }
364
365  public void testRoundLog2ThrowsOnNegative() {
366    for (RoundingMode mode : ALL_ROUNDING_MODES) {
367      for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
368        try {
369          DoubleMath.log2(-d, mode);
370          fail("Expected IllegalArgumentException");
371        } catch (IllegalArgumentException e) {}
372      }
373    }
374  }
375
376  public void testIsPowerOfTwoYes() {
377    for (int i = -1074; i <= 1023; i++) {
378      assertTrue(DoubleMath.isPowerOfTwo(StrictMath.pow(2.0, i)));
379    }
380  }
381
382  public void testIsPowerOfTwo() {
383    for (double x : ALL_DOUBLE_CANDIDATES) {
384      boolean expected = x > 0 && !Double.isInfinite(x) && !Double.isNaN(x)
385          && StrictMath.pow(2.0, DoubleMath.log2(x, FLOOR)) == x;
386      assertEquals(expected, DoubleMath.isPowerOfTwo(x));
387    }
388  }
389
390  public void testLog2Accuracy() {
391    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
392      double dmLog2 = DoubleMath.log2(d);
393      double trueLog2 = trueLog2(d);
394      assertTrue(Math.abs(dmLog2 - trueLog2) <= Math.ulp(trueLog2));
395    }
396  }
397
398  public void testLog2SemiMonotonic(){
399    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
400      assertTrue(DoubleMath.log2(d + 0.01) >= DoubleMath.log2(d));
401    }
402  }
403
404  public void testLog2Negative() {
405    for (double d : POSITIVE_FINITE_DOUBLE_CANDIDATES) {
406      assertTrue(Double.isNaN(DoubleMath.log2(-d)));
407    }
408  }
409
410  public void testLog2Zero() {
411    assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(0.0));
412    assertEquals(Double.NEGATIVE_INFINITY, DoubleMath.log2(-0.0));
413  }
414
415  public void testLog2NaNInfinity() {
416    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.log2(Double.POSITIVE_INFINITY));
417    assertTrue(Double.isNaN(DoubleMath.log2(Double.NEGATIVE_INFINITY)));
418    assertTrue(Double.isNaN(DoubleMath.log2(Double.NaN)));
419  }
420
421  private strictfp double trueLog2(double d) {
422    double trueLog2 = StrictMath.log(d) / StrictMath.log(2);
423    // increment until it's >= the true value
424    while (StrictMath.pow(2.0, trueLog2) < d) {
425      trueLog2 = StrictMath.nextUp(trueLog2);
426    }
427    // decrement until it's <= the true value
428    while (StrictMath.pow(2.0, trueLog2) > d) {
429      trueLog2 = StrictMath.nextAfter(trueLog2, Double.NEGATIVE_INFINITY);
430    }
431    if (StrictMath.abs(StrictMath.pow(2.0, trueLog2) - d)
432        > StrictMath.abs(StrictMath.pow(2.0, StrictMath.nextUp(trueLog2)) - d)) {
433      trueLog2 = StrictMath.nextUp(trueLog2);
434    }
435    return trueLog2;
436  }
437
438  public void testIsMathematicalIntegerIntegral() {
439    for (double d : INTEGRAL_DOUBLE_CANDIDATES) {
440      assertTrue(DoubleMath.isMathematicalInteger(d));
441    }
442  }
443
444  public void testIsMathematicalIntegerFractional() {
445    for (double d : FRACTIONAL_DOUBLE_CANDIDATES) {
446      assertFalse(DoubleMath.isMathematicalInteger(d));
447    }
448  }
449
450  public void testIsMathematicalIntegerNotFinite() {
451    for (double d :
452        Arrays.asList(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN)) {
453      assertFalse(DoubleMath.isMathematicalInteger(d));
454    }
455  }
456
457  public void testFactorial() {
458    for (int i = 0; i <= DoubleMath.MAX_FACTORIAL; i++) {
459      double actual = BigIntegerMath.factorial(i).doubleValue();
460      double result = DoubleMath.factorial(i);
461      assertEquals(actual, result, Math.ulp(actual));
462    }
463  }
464
465  public void testFactorialTooHigh() {
466    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 1));
467    assertEquals(Double.POSITIVE_INFINITY, DoubleMath.factorial(DoubleMath.MAX_FACTORIAL + 20));
468  }
469
470  public void testFactorialNegative() {
471    for (int n : NEGATIVE_INTEGER_CANDIDATES) {
472      try {
473        DoubleMath.factorial(n);
474        fail("Expected IllegalArgumentException");
475      } catch (IllegalArgumentException expected) {}
476    }
477  }
478
479  public void testNullPointers() throws Exception {
480    NullPointerTester tester = new NullPointerTester();
481    tester.setDefault(RoundingMode.class, FLOOR);
482    tester.setDefault(double.class, 3.0);
483    tester.testAllPublicStaticMethods(DoubleMath.class);
484  }
485}
486