|
22 | 22 | import static com.google.common.math.MathTesting.NEGATIVE_BIGINTEGER_CANDIDATES; |
23 | 23 | import static com.google.common.math.MathTesting.NONZERO_BIGINTEGER_CANDIDATES; |
24 | 24 | import static com.google.common.math.MathTesting.POSITIVE_BIGINTEGER_CANDIDATES; |
| 25 | +import static com.google.common.truth.Truth.assertThat; |
| 26 | +import static com.google.common.truth.Truth.assertWithMessage; |
25 | 27 | import static java.math.BigInteger.ONE; |
26 | 28 | import static java.math.BigInteger.TEN; |
27 | 29 | import static java.math.BigInteger.ZERO; |
|
33 | 35 | import static java.math.RoundingMode.HALF_UP; |
34 | 36 | import static java.math.RoundingMode.UNNECESSARY; |
35 | 37 | import static java.math.RoundingMode.UP; |
| 38 | +import static java.math.RoundingMode.values; |
36 | 39 | import static java.util.Arrays.asList; |
37 | 40 |
|
38 | 41 | import com.google.common.annotations.GwtCompatible; |
|
41 | 44 | import java.math.BigDecimal; |
42 | 45 | import java.math.BigInteger; |
43 | 46 | import java.math.RoundingMode; |
| 47 | +import java.util.EnumMap; |
| 48 | +import java.util.EnumSet; |
| 49 | +import java.util.Map; |
44 | 50 | import junit.framework.TestCase; |
45 | 51 |
|
46 | 52 | /** |
@@ -542,6 +548,219 @@ public void testBinomialOutside() { |
542 | 548 | } |
543 | 549 | } |
544 | 550 |
|
| 551 | + @GwtIncompatible |
| 552 | + private static final class RoundToDoubleTester { |
| 553 | + private final BigInteger input; |
| 554 | + private final Map<RoundingMode, Double> expectedValues = new EnumMap<>(RoundingMode.class); |
| 555 | + private boolean unnecessaryShouldThrow = false; |
| 556 | + |
| 557 | + RoundToDoubleTester(BigInteger input) { |
| 558 | + this.input = input; |
| 559 | + } |
| 560 | + |
| 561 | + RoundToDoubleTester setExpectation(double expectedValue, RoundingMode... modes) { |
| 562 | + for (RoundingMode mode : modes) { |
| 563 | + Double previous = expectedValues.put(mode, expectedValue); |
| 564 | + if (previous != null) { |
| 565 | + throw new AssertionError(); |
| 566 | + } |
| 567 | + } |
| 568 | + return this; |
| 569 | + } |
| 570 | + |
| 571 | + public RoundToDoubleTester roundUnnecessaryShouldThrow() { |
| 572 | + unnecessaryShouldThrow = true; |
| 573 | + return this; |
| 574 | + } |
| 575 | + |
| 576 | + public void test() { |
| 577 | + assertThat(expectedValues.keySet()) |
| 578 | + .containsAtLeastElementsIn(EnumSet.complementOf(EnumSet.of(UNNECESSARY))); |
| 579 | + for (Map.Entry<RoundingMode, Double> entry : expectedValues.entrySet()) { |
| 580 | + RoundingMode mode = entry.getKey(); |
| 581 | + Double expectation = entry.getValue(); |
| 582 | + assertWithMessage("roundToDouble(" + input + ", " + mode + ")") |
| 583 | + .that(BigIntegerMath.roundToDouble(input, mode)) |
| 584 | + .isEqualTo(expectation); |
| 585 | + } |
| 586 | + |
| 587 | + if (!expectedValues.containsKey(UNNECESSARY)) { |
| 588 | + assertWithMessage("Expected roundUnnecessaryShouldThrow call") |
| 589 | + .that(unnecessaryShouldThrow) |
| 590 | + .isTrue(); |
| 591 | + try { |
| 592 | + BigIntegerMath.roundToDouble(input, UNNECESSARY); |
| 593 | + fail("Expected ArithmeticException for roundToDouble(" + input + ", UNNECESSARY)"); |
| 594 | + } catch (ArithmeticException expected) { |
| 595 | + // expected |
| 596 | + } |
| 597 | + } |
| 598 | + } |
| 599 | + } |
| 600 | + |
| 601 | + @GwtIncompatible |
| 602 | + public void testRoundToDouble_Zero() { |
| 603 | + new RoundToDoubleTester(BigInteger.ZERO).setExpectation(0.0, values()).test(); |
| 604 | + } |
| 605 | + |
| 606 | + @GwtIncompatible |
| 607 | + public void testRoundToDouble_smallPositive() { |
| 608 | + new RoundToDoubleTester(BigInteger.valueOf(16)).setExpectation(16.0, values()).test(); |
| 609 | + } |
| 610 | + |
| 611 | + @GwtIncompatible |
| 612 | + public void testRoundToDouble_maxPreciselyRepresentable() { |
| 613 | + new RoundToDoubleTester(BigInteger.valueOf(1L << 53)) |
| 614 | + .setExpectation(Math.pow(2, 53), values()) |
| 615 | + .test(); |
| 616 | + } |
| 617 | + |
| 618 | + @GwtIncompatible |
| 619 | + public void testRoundToDouble_maxPreciselyRepresentablePlusOne() { |
| 620 | + double twoToThe53 = Math.pow(2, 53); |
| 621 | + // the representable doubles are 2^53 and 2^53 + 2. |
| 622 | + // 2^53+1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. |
| 623 | + new RoundToDoubleTester(BigInteger.valueOf((1L << 53) + 1)) |
| 624 | + .setExpectation(twoToThe53, DOWN, FLOOR, HALF_DOWN, HALF_EVEN) |
| 625 | + .setExpectation(Math.nextUp(twoToThe53), CEILING, UP, HALF_UP) |
| 626 | + .roundUnnecessaryShouldThrow() |
| 627 | + .test(); |
| 628 | + } |
| 629 | + |
| 630 | + @GwtIncompatible |
| 631 | + public void testRoundToDouble_twoToThe54PlusOne() { |
| 632 | + double twoToThe54 = Math.pow(2, 54); |
| 633 | + // the representable doubles are 2^54 and 2^54 + 4 |
| 634 | + // 2^54+1 is less than halfway between, so HALF_DOWN and HALF_UP will both go down. |
| 635 | + new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 1)) |
| 636 | + .setExpectation(twoToThe54, DOWN, FLOOR, HALF_DOWN, HALF_UP, HALF_EVEN) |
| 637 | + .setExpectation(Math.nextUp(twoToThe54), CEILING, UP) |
| 638 | + .roundUnnecessaryShouldThrow() |
| 639 | + .test(); |
| 640 | + } |
| 641 | + |
| 642 | + @GwtIncompatible |
| 643 | + public void testRoundToDouble_twoToThe54PlusThree() { |
| 644 | + double twoToThe54 = Math.pow(2, 54); |
| 645 | + // the representable doubles are 2^54 and 2^54 + 4 |
| 646 | + // 2^54+3 is more than halfway between, so HALF_DOWN and HALF_UP will both go up. |
| 647 | + new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 3)) |
| 648 | + .setExpectation(twoToThe54, DOWN, FLOOR) |
| 649 | + .setExpectation(Math.nextUp(twoToThe54), CEILING, UP, HALF_DOWN, HALF_UP, HALF_EVEN) |
| 650 | + .roundUnnecessaryShouldThrow() |
| 651 | + .test(); |
| 652 | + } |
| 653 | + |
| 654 | + @GwtIncompatible |
| 655 | + public void testRoundToDouble_twoToThe54PlusFour() { |
| 656 | + new RoundToDoubleTester(BigInteger.valueOf((1L << 54) + 4)) |
| 657 | + .setExpectation(Math.pow(2, 54) + 4, values()) |
| 658 | + .test(); |
| 659 | + } |
| 660 | + |
| 661 | + @GwtIncompatible |
| 662 | + public void testRoundToDouble_maxDouble() { |
| 663 | + BigInteger maxDoubleAsBI = DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY); |
| 664 | + new RoundToDoubleTester(maxDoubleAsBI).setExpectation(Double.MAX_VALUE, values()).test(); |
| 665 | + } |
| 666 | + |
| 667 | + @GwtIncompatible |
| 668 | + public void testRoundToDouble_maxDoublePlusOne() { |
| 669 | + BigInteger maxDoubleAsBI = |
| 670 | + DoubleMath.roundToBigInteger(Double.MAX_VALUE, UNNECESSARY).add(BigInteger.ONE); |
| 671 | + new RoundToDoubleTester(maxDoubleAsBI) |
| 672 | + .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) |
| 673 | + .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) |
| 674 | + .roundUnnecessaryShouldThrow() |
| 675 | + .test(); |
| 676 | + } |
| 677 | + |
| 678 | + @GwtIncompatible |
| 679 | + public void testRoundToDouble_wayTooBig() { |
| 680 | + BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT); |
| 681 | + new RoundToDoubleTester(bi) |
| 682 | + .setExpectation(Double.MAX_VALUE, DOWN, FLOOR, HALF_EVEN, HALF_UP, HALF_DOWN) |
| 683 | + .setExpectation(Double.POSITIVE_INFINITY, UP, CEILING) |
| 684 | + .roundUnnecessaryShouldThrow() |
| 685 | + .test(); |
| 686 | + } |
| 687 | + |
| 688 | + @GwtIncompatible |
| 689 | + public void testRoundToDouble_smallNegative() { |
| 690 | + new RoundToDoubleTester(BigInteger.valueOf(-16)).setExpectation(-16.0, values()).test(); |
| 691 | + } |
| 692 | + |
| 693 | + @GwtIncompatible |
| 694 | + public void testRoundToDouble_minPreciselyRepresentable() { |
| 695 | + new RoundToDoubleTester(BigInteger.valueOf(-1L << 53)) |
| 696 | + .setExpectation(-Math.pow(2, 53), values()) |
| 697 | + .test(); |
| 698 | + } |
| 699 | + |
| 700 | + @GwtIncompatible |
| 701 | + public void testRoundToDouble_minPreciselyRepresentableMinusOne() { |
| 702 | + // the representable doubles are -2^53 and -2^53 - 2. |
| 703 | + // -2^53-1 is halfway between, so HALF_UP will go up and HALF_DOWN will go down. |
| 704 | + new RoundToDoubleTester(BigInteger.valueOf((-1L << 53) - 1)) |
| 705 | + .setExpectation(-Math.pow(2, 53), DOWN, CEILING, HALF_DOWN, HALF_EVEN) |
| 706 | + .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 53)), FLOOR, UP, HALF_UP) |
| 707 | + .roundUnnecessaryShouldThrow() |
| 708 | + .test(); |
| 709 | + } |
| 710 | + |
| 711 | + @GwtIncompatible |
| 712 | + public void testRoundToDouble_negativeTwoToThe54MinusOne() { |
| 713 | + new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 1)) |
| 714 | + .setExpectation(-Math.pow(2, 54), DOWN, CEILING, HALF_DOWN, HALF_UP, HALF_EVEN) |
| 715 | + .setExpectation(DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP) |
| 716 | + .roundUnnecessaryShouldThrow() |
| 717 | + .test(); |
| 718 | + } |
| 719 | + |
| 720 | + @GwtIncompatible |
| 721 | + public void testRoundToDouble_negativeTwoToThe54MinusThree() { |
| 722 | + new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 3)) |
| 723 | + .setExpectation(-Math.pow(2, 54), DOWN, CEILING) |
| 724 | + .setExpectation( |
| 725 | + DoubleUtils.nextDown(-Math.pow(2, 54)), FLOOR, UP, HALF_DOWN, HALF_UP, HALF_EVEN) |
| 726 | + .roundUnnecessaryShouldThrow() |
| 727 | + .test(); |
| 728 | + } |
| 729 | + |
| 730 | + @GwtIncompatible |
| 731 | + public void testRoundToDouble_negativeTwoToThe54MinusFour() { |
| 732 | + new RoundToDoubleTester(BigInteger.valueOf((-1L << 54) - 4)) |
| 733 | + .setExpectation(-Math.pow(2, 54) - 4, values()) |
| 734 | + .test(); |
| 735 | + } |
| 736 | + |
| 737 | + @GwtIncompatible |
| 738 | + public void testRoundToDouble_minDouble() { |
| 739 | + BigInteger minDoubleAsBI = DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY); |
| 740 | + new RoundToDoubleTester(minDoubleAsBI).setExpectation(-Double.MAX_VALUE, values()).test(); |
| 741 | + } |
| 742 | + |
| 743 | + @GwtIncompatible |
| 744 | + public void testRoundToDouble_minDoubleMinusOne() { |
| 745 | + BigInteger minDoubleAsBI = |
| 746 | + DoubleMath.roundToBigInteger(-Double.MAX_VALUE, UNNECESSARY).subtract(BigInteger.ONE); |
| 747 | + new RoundToDoubleTester(minDoubleAsBI) |
| 748 | + .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) |
| 749 | + .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) |
| 750 | + .roundUnnecessaryShouldThrow() |
| 751 | + .test(); |
| 752 | + } |
| 753 | + |
| 754 | + @GwtIncompatible |
| 755 | + public void testRoundToDouble_negativeWayTooBig() { |
| 756 | + BigInteger bi = BigInteger.ONE.shiftLeft(2 * Double.MAX_EXPONENT).negate(); |
| 757 | + new RoundToDoubleTester(bi) |
| 758 | + .setExpectation(-Double.MAX_VALUE, DOWN, CEILING, HALF_EVEN, HALF_UP, HALF_DOWN) |
| 759 | + .setExpectation(Double.NEGATIVE_INFINITY, UP, FLOOR) |
| 760 | + .roundUnnecessaryShouldThrow() |
| 761 | + .test(); |
| 762 | + } |
| 763 | + |
545 | 764 | @GwtIncompatible // NullPointerTester |
546 | 765 | public void testNullPointers() { |
547 | 766 | NullPointerTester tester = new NullPointerTester(); |
|
0 commit comments