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