Home | History | Annotate | Download | only in math
      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 
     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