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 
main(String... args)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 += lowPrecisionPerfectSquares();
43 
44         if (failures > 0 ) {
45             throw new RuntimeException("Incurred " + failures + " failures" +
46                                        " testing BigDecimal.sqrt().");
47         }
48     }
49 
negativeTests()50     private static int negativeTests() {
51         int failures = 0;
52 
53         for (long i = -10; i < 0; i++) {
54             for (int j = -5; j < 5; j++) {
55                 try {
56                     BigDecimal input = BigDecimal.valueOf(i, j);
57                     BigDecimal result = input.sqrt(MathContext.DECIMAL64);
58                     System.err.println("Unexpected sqrt of negative: (" +
59                                        input + ").sqrt()  = " + result );
60                     failures += 1;
61                 } catch (ArithmeticException e) {
62                     ; // Expected
63                 }
64             }
65         }
66 
67         return failures;
68     }
69 
zeroTests()70     private static int zeroTests() {
71         int failures = 0;
72 
73         for (int i = -100; i < 100; i++) {
74             BigDecimal expected = BigDecimal.valueOf(0L, i/2);
75             // These results are independent of rounding mode
76             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.UNLIMITED),
77                                 expected, true, "zeros");
78 
79             failures += compare(BigDecimal.valueOf(0L, i).sqrt(MathContext.DECIMAL64),
80                                 expected, true, "zeros");
81         }
82 
83         return failures;
84     }
85 
86     /**
87      * sqrt(10^2N) is 10^N
88      * Both numerical value and representation should be verified
89      */
evenPowersOfTenTests()90     private static int evenPowersOfTenTests() {
91         int failures = 0;
92         MathContext oneDigitExactly = new MathContext(1, RoundingMode.UNNECESSARY);
93 
94         for (int scale = -100; scale <= 100; scale++) {
95             BigDecimal testValue       = BigDecimal.valueOf(1, 2*scale);
96             BigDecimal expectedNumericalResult = BigDecimal.valueOf(1, scale);
97 
98             BigDecimal result;
99 
100 
101             failures += equalNumerically(expectedNumericalResult,
102                                            result = testValue.sqrt(MathContext.DECIMAL64),
103                                            "Even powers of 10, DECIMAL64");
104 
105             // Can round to one digit of precision exactly
106             failures += equalNumerically(expectedNumericalResult,
107                                            result = testValue.sqrt(oneDigitExactly),
108                                            "even powers of 10, 1 digit");
109             if (result.precision() > 1) {
110                 failures += 1;
111                 System.err.println("Excess precision for " + result);
112             }
113 
114 
115             // If rounding to more than one digit, do precision / scale checking...
116 
117         }
118 
119         return failures;
120     }
121 
squareRootTwoTests()122     private static int squareRootTwoTests() {
123         int failures = 0;
124         BigDecimal TWO = new BigDecimal(2);
125 
126         // Square root of 2 truncated to 65 digits
127         BigDecimal highPrecisionRoot2 =
128             new BigDecimal("1.41421356237309504880168872420969807856967187537694807317667973799");
129 
130 
131         RoundingMode[] modes = {
132             RoundingMode.UP,       RoundingMode.DOWN,
133             RoundingMode.CEILING, RoundingMode.FLOOR,
134             RoundingMode.HALF_UP, RoundingMode.HALF_DOWN, RoundingMode.HALF_EVEN
135         };
136 
137         // For each iteresting rounding mode, for precisions 1 to, say
138         // 63 numerically compare TWO.sqrt(mc) to
139         // highPrecisionRoot2.round(mc)
140 
141         for (RoundingMode mode : modes) {
142             for (int precision = 1; precision < 63; precision++) {
143                 MathContext mc = new MathContext(precision, mode);
144                 BigDecimal expected = highPrecisionRoot2.round(mc);
145                 BigDecimal computed = TWO.sqrt(mc);
146 
147                 equalNumerically(expected, computed, "sqrt(2)");
148             }
149         }
150 
151         return failures;
152     }
153 
lowPrecisionPerfectSquares()154     private static int lowPrecisionPerfectSquares() {
155         int failures = 0;
156 
157         // For 5^2 through 9^2, if the input is rounded to one digit
158         // first before the root is computed, the wrong answer will
159         // result. Verify results and scale for different rounding
160         // modes and precisions.
161         long[][] squaresWithOneDigitRoot = {{ 4, 2},
162                                             { 9, 3},
163                                             {25, 5},
164                                             {36, 6},
165                                             {49, 7},
166                                             {64, 8},
167                                             {81, 9}};
168 
169         for (long[] squareAndRoot : squaresWithOneDigitRoot) {
170             BigDecimal square     = new BigDecimal(squareAndRoot[0]);
171             BigDecimal expected   = new BigDecimal(squareAndRoot[1]);
172 
173             for (int scale = 0; scale <= 4; scale++) {
174                 BigDecimal scaledSquare = square.setScale(scale, RoundingMode.UNNECESSARY);
175                 int expectedScale = scale/2;
176                 for (int precision = 0; precision <= 5; precision++) {
177                     for (RoundingMode rm : RoundingMode.values()) {
178                         MathContext mc = new MathContext(precision, rm);
179                         BigDecimal computedRoot = scaledSquare.sqrt(mc);
180                         failures += equalNumerically(expected, computedRoot, "simple squares");
181                         int computedScale = computedRoot.scale();
182                         if (precision >=  expectedScale + 1 &&
183                             computedScale != expectedScale) {
184                         System.err.printf("%s\tprecision=%d\trm=%s%n",
185                                           computedRoot.toString(), precision, rm);
186                             failures++;
187                             System.err.printf("\t%s does not have expected scale of %d%n.",
188                                               computedRoot, expectedScale);
189                         }
190                     }
191                 }
192             }
193         }
194 
195         return failures;
196     }
197 
compare(BigDecimal a, BigDecimal b, boolean expected, String prefix)198     private static int compare(BigDecimal a, BigDecimal b, boolean expected, String prefix) {
199         boolean result = a.equals(b);
200         int failed = (result==expected) ? 0 : 1;
201         if (failed == 1) {
202             System.err.println("Testing " + prefix +
203                                "(" + a + ").compareTo(" + b + ") => " + result +
204                                "\n\tExpected " + expected);
205         }
206         return failed;
207     }
208 
equalNumerically(BigDecimal a, BigDecimal b, String prefix)209     private static int equalNumerically(BigDecimal a, BigDecimal b,
210                                         String prefix) {
211         return compareNumerically(a, b, 0, prefix);
212     }
213 
214 
compareNumerically(BigDecimal a, BigDecimal b, int expected, String prefix)215     private static int compareNumerically(BigDecimal a, BigDecimal b,
216                                           int expected, String prefix) {
217         int result = a.compareTo(b);
218         int failed = (result==expected) ? 0 : 1;
219         if (failed == 1) {
220             System.err.println("Testing " + prefix +
221                                "(" + a + ").compareTo(" + b + ") => " + result +
222                                "\n\tExpected " + expected);
223         }
224         return failed;
225     }
226 
227 }
228