• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.google.common.math;
18 
19 import static com.google.common.math.MathTesting.ALL_DOUBLE_CANDIDATES;
20 import static com.google.common.math.MathTesting.ALL_ROUNDING_MODES;
21 import static com.google.common.math.MathTesting.ALL_SAFE_ROUNDING_MODES;
22 import static com.google.common.math.MathTesting.FRACTIONAL_DOUBLE_CANDIDATES;
23 import static com.google.common.math.MathTesting.INTEGRAL_DOUBLE_CANDIDATES;
24 import static com.google.common.math.MathTesting.NEGATIVE_INTEGER_CANDIDATES;
25 import static com.google.common.math.MathTesting.POSITIVE_FINITE_DOUBLE_CANDIDATES;
26 import static java.math.RoundingMode.CEILING;
27 import static java.math.RoundingMode.DOWN;
28 import static java.math.RoundingMode.FLOOR;
29 import static java.math.RoundingMode.HALF_DOWN;
30 import static java.math.RoundingMode.HALF_EVEN;
31 import static java.math.RoundingMode.HALF_UP;
32 import static java.math.RoundingMode.UNNECESSARY;
33 import static java.math.RoundingMode.UP;
34 import static java.util.Arrays.asList;
35 
36 import com.google.common.testing.NullPointerTester;
37 
38 import junit.framework.TestCase;
39 
40 import java.math.BigDecimal;
41 import java.math.BigInteger;
42 import java.math.RoundingMode;
43 import java.util.Arrays;
44 
45 /**
46  * Tests for {@code DoubleMath}.
47  *
48  * @author Louis Wasserman
49  */
50 public 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 
testConstantsMaxFactorial()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 
testConstantsEverySixteenthFactorial()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 
testRoundIntegralDoubleToInt()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 
testRoundFractionalDoubleToInt()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 
testRoundExactIntegralDoubleToInt()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 
testRoundExactFractionalDoubleToIntFails()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 
testRoundNaNToIntAlwaysFails()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 
testRoundInfiniteToIntAlwaysFails()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 
testRoundIntegralDoubleToLong()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 
testRoundFractionalDoubleToLong()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 
testRoundExactIntegralDoubleToLong()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 
testRoundExactFractionalDoubleToLongFails()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 
testRoundNaNToLongAlwaysFails()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 
testRoundInfiniteToLongAlwaysFails()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 
testRoundIntegralDoubleToBigInteger()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 
testRoundFractionalDoubleToBigInteger()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 
testRoundExactIntegralDoubleToBigInteger()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 
testRoundExactFractionalDoubleToBigIntegerFails()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 
testRoundNaNToBigIntegerAlwaysFails()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 
testRoundInfiniteToBigIntegerAlwaysFails()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 
testRoundLog2Floor()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 
testRoundLog2Ceiling()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