1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4851777
  27  * @summary Tests of BigDecimal.sqrt().
  28  */
  29 
  30 import java.math.*;
  31 import java.util.*;
  32 
  33 public class SquareRootTests {
  34 
  35     public static void main(String... args) {
  36         int failures = 0;
  37 
  38         failures += negativeTests();
  39         failures += zeroTests();
  40         failures += evenPowersOfTenTests();
  41         failures += squareRootTwoTests();
  42         failures += lowPrecisionPerjectSquares();
  43 
  44         // Add trickier rounding cases -- try to find examples of
  45 
  46         // Add direct testing of definition of rounding...
  47         // result should be closer than result+ulp or result - ulp,
  48         // depending on rounding mode, etc.
  49 
  50         if (failures > 0 ) {
  51             throw new RuntimeException("Incurred " + failures + " failures" +
  52                                        " testing BigDecimal.sqrt().");
  53         }
  54     }
  55 
  56     private static int negativeTests() {
  57         int failures = 0;
  58 
  59         for (long i = -10; i < 0; i++) {
  60             for (int j = -5; j < 5; j++) {
  61                 try {
  62                     BigDecimal input = BigDecimal.valueOf(i, j);
  63                     BigDecimal result = input.sqrt(MathContext.DECIMAL64);
  64                     System.err.println("Unexpected sqrt of negative: (" +
  65                                        input + ").sqrt()  = " + result );
  66                     failures += 1;
  67                 } catch (ArithmeticException e) {
  68                     ; // Expected
  69                 }
  70             }
  71         }
  72         
  73         return failures;
  74     }
  75 
  76     private static int zeroTests() {
  77         int failures = 0;
  78 
  79         for (int i = -100; i < 100; i++) {
  80             BigDecimal expected = BigDecimal.valueOf(0L, i/2);
  81             // These results are independent of rounding mode
  82             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.UNLIMITED),
  83                                 expected, true, "zeros");
  84 
  85             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.DECIMAL64),
  86                                 expected, true, "zeros");
  87         }
  88 
  89         return failures;
  90     }
  91 
  92     /**
  93      * sqrt(10^2N) is 10^N
  94      * Both numerical value and representation should be verified
  95      */
  96     private static int evenPowersOfTenTests() {
  97         int failures = 0;
  98         MathContext oneDigitExactly = new MathContext(1, RoundingMode.UNNECESSARY);
  99 
 100         for (int scale = -100; scale <= 100; scale++) {
 101             BigDecimal testValue       = BigDecimal.valueOf(1, 2*scale);
 102             BigDecimal expectedNumericalResult = BigDecimal.valueOf(1, scale);
 103 
 104             BigDecimal result;
 105 
 106 
 107             failures += equalNumerically(expectedNumericalResult,
 108                                            result = testValue.sqrt(MathContext.DECIMAL64),
 109                                            "Even powers of 10, DECIMAL64");
 110 
 111             // Can round to one digit of precision exactly
 112             failures += equalNumerically(expectedNumericalResult,
 113                                            result = testValue.sqrt(oneDigitExactly),
 114                                            "even powers of 10, 1 digit");
 115             if (result.precision() > 1) {
 116                 failures += 1;
 117                 System.err.println("Excess precision for " + result);
 118             }
 119                 
 120 
 121             // If rounding to more than one digit, do precision / scale checking...
 122 
 123         }
 124 
 125         return failures;
 126     }
 127 
 128     private static int squareRootTwoTests() {
 129         int failures = 0;
 130         BigDecimal TWO = new BigDecimal(2);
 131 
 132         // Square root of 2 truncated to 65 digits
 133         BigDecimal highPrecisionRoot2 =
 134             new BigDecimal("1.41421356237309504880168872420969807856967187537694807317667973799");
 135 
 136 
 137         RoundingMode[] modes = {
 138             RoundingMode.UP,       RoundingMode.DOWN,
 139             RoundingMode.CEILING, RoundingMode.FLOOR,
 140             RoundingMode.HALF_UP, RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN
 141         };
 142 
 143         // For each iteresting rounding mode, for precisions 1 to, say
 144         // 63 numerically compare TWO.sqrt(mc) to
 145         // highPrecisionRoot2.round(mc)
 146         
 147         for (RoundingMode mode : modes) {
 148             for (int precision = 1; precision < 63; precision++) {
 149                 MathContext mc = new MathContext(precision, mode);
 150                 BigDecimal expected = highPrecisionRoot2.round(mc); 
 151                 BigDecimal computed = TWO.sqrt(mc);
 152 
 153                 equalNumerically(expected, computed, "sqrt(2)");
 154             }
 155         }
 156 
 157         return failures;
 158     }
 159 
 160             // When setting the precision to use inside the loop, take
 161             // care to avoid the case where the precision of the input
 162             // exceeds the requested precision and rounding the input
 163             // value too soon.
 164         // For example sqrt(16) rounded to one digit should be 4 not
 165         // 3; if 16 was rounded down to one digit first, the the returned value might be 3
 166         // Similar case for 25, 36, 49, 64, 81, 
 167 
 168     private static int lowPrecisionPerfectSquares() {
 169         int failures = 0;
 170 
 171         return failures;
 172     }
 173 
 174     private static int compare(BigDecimal a, BigDecimal b, boolean expected, String prefix) {
 175         boolean result = a.equals(b);
 176         int failed = (result==expected) ? 0 : 1;
 177         if (failed == 1) {
 178             System.err.println("Testing " + prefix + 
 179                                "(" + a + ").compareTo(" + b + ") => " + result +
 180                                "\n\tExpected " + expected);
 181         }
 182         return failed;
 183     }
 184 
 185     private static int equalNumerically(BigDecimal a, BigDecimal b,
 186                                         String prefix) {
 187         return compareNumerically(a, b, 0, prefix);
 188     }
 189 
 190 
 191     private static int compareNumerically(BigDecimal a, BigDecimal b,
 192                                           int expected, String prefix) {
 193         int result = a.compareTo(b);
 194         int failed = (result==expected) ? 0 : 1;
 195         if (failed == 1) {
 196             System.err.println("Testing " + prefix + 
 197                                "(" + a + ").compareTo(" + b + ") => " + result +
 198                                "\n\tExpected " + expected);
 199         }
 200         return failed;
 201     }
 202 
 203 }