blob: e988ca554d6f0b8d11ecd1ae968110f0bf93477a [file] [log] [blame]
darcy32db4492009-01-26 19:49:26 -08001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/*
25 * @test
26 * @bug 4851776 4907265 6177836
27 * @summary Some tests for the divide methods.
28 * @author Joseph D. Darcy
29 * @compile -source 1.5 DivideTests.java
30 * @run main DivideTests
31 */
32
33import java.math.*;
34import static java.math.BigDecimal.*;
35
36public class DivideTests {
37
38 // Preliminary exact divide method; could be used for comparison
39 // purposes.
40 BigDecimal anotherDivide(BigDecimal dividend, BigDecimal divisor) {
41 /*
42 * Handle zero cases first.
43 */
44 if (divisor.signum() == 0) { // x/0
45 if (dividend.signum() == 0) // 0/0
46 throw new ArithmeticException("Division undefined"); // NaN
47 throw new ArithmeticException("Division by zero");
48 }
49 if (dividend.signum() == 0) // 0/y
50 return BigDecimal.ZERO;
51 else {
52 /*
53 * Determine if there is a result with a terminating
54 * decimal expansion. Putting aside overflow and
55 * underflow considerations, the existance of an exact
56 * result only depends on the ratio of the intVal's of the
57 * dividend (i.e. this) and and divisor since the scales
58 * of the argument just affect where the decimal point
59 * lies.
60 *
61 * For the ratio of (a = this.intVal) and (b =
62 * divisor.intVal) to have a finite decimal expansion,
63 * once a/b is put in lowest terms, b must be equal to
64 * (2^i)*(5^j) for some integer i,j >= 0. Therefore, we
65 * first compute to see if b_prime =(b/gcd(a,b)) is equal
66 * to (2^i)*(5^j).
67 */
68 BigInteger TWO = BigInteger.valueOf(2);
69 BigInteger FIVE = BigInteger.valueOf(5);
70 BigInteger TEN = BigInteger.valueOf(10);
71
72 BigInteger divisorIntvalue = divisor.scaleByPowerOfTen(divisor.scale()).toBigInteger().abs();
73 BigInteger dividendIntvalue = dividend.scaleByPowerOfTen(dividend.scale()).toBigInteger().abs();
74
75 BigInteger b_prime = divisorIntvalue.divide(dividendIntvalue.gcd(divisorIntvalue));
76
77 boolean goodDivisor = false;
78 int i=0, j=0;
79
80 badDivisor: {
81 while(! b_prime.equals(BigInteger.ONE) ) {
82 int b_primeModTen = b_prime.mod(TEN).intValue() ;
83
84 switch(b_primeModTen) {
85 case 0:
86 // b_prime divisible by 10=2*5, increment i and j
87 i++;
88 j++;
89 b_prime = b_prime.divide(TEN);
90 break;
91
92 case 5:
93 // b_prime divisible by 5, increment j
94 j++;
95 b_prime = b_prime.divide(FIVE);
96 break;
97
98 case 2:
99 case 4:
100 case 6:
101 case 8:
102 // b_prime divisible by 2, increment i
103 i++;
104 b_prime = b_prime.divide(TWO);
105 break;
106
107 default: // hit something we shouldn't have
108 b_prime = BigInteger.ONE; // terminate loop
109 break badDivisor;
110 }
111 }
112
113 goodDivisor = true;
114 }
115
116 if( ! goodDivisor ) {
117 throw new ArithmeticException("Non terminating decimal expansion");
118 }
119 else {
120 // What is a rule for determining how many digits are
121 // needed? Once that is determined, cons up a new
122 // MathContext object and pass it on to the divide(bd,
123 // mc) method; precision == ?, roundingMode is unnecessary.
124
125 // Are we sure this is the right scale to use? Should
126 // also determine a precision-based method.
127 MathContext mc = new MathContext(dividend.precision() +
128 (int)Math.ceil(
129 10.0*divisor.precision()/3.0),
130 RoundingMode.UNNECESSARY);
131 // Should do some more work here to rescale, etc.
132 return dividend.divide(divisor, mc);
133 }
134 }
135 }
136
137 public static int powersOf2and5() {
138 int failures = 0;
139
140 for(int i = 0; i < 6; i++) {
141 int powerOf2 = (int)StrictMath.pow(2.0, i);
142
143 for(int j = 0; j < 6; j++) {
144 int powerOf5 = (int)StrictMath.pow(5.0, j);
145 int product;
146
147 BigDecimal bd;
148
149 try {
150 bd = BigDecimal.ONE.divide(new BigDecimal(product=powerOf2*powerOf5));
151 } catch (ArithmeticException e) {
152 failures++;
153 System.err.println((new BigDecimal(powerOf2)).toString() + " / " +
154 (new BigDecimal(powerOf5)).toString() + " threw an exception.");
155 e.printStackTrace();
156 }
157
158 try {
159 bd = new BigDecimal(powerOf2).divide(new BigDecimal(powerOf5));
160 } catch (ArithmeticException e) {
161 failures++;
162 System.err.println((new BigDecimal(powerOf2)).toString() + " / " +
163 (new BigDecimal(powerOf5)).toString() + " threw an exception.");
164 e.printStackTrace();
165 }
166
167 try {
168 bd = new BigDecimal(powerOf5).divide(new BigDecimal(powerOf2));
169 } catch (ArithmeticException e) {
170 failures++;
171 System.err.println((new BigDecimal(powerOf5)).toString() + " / " +
172 (new BigDecimal(powerOf2)).toString() + " threw an exception.");
173
174 e.printStackTrace();
175 }
176
177 }
178 }
179 return failures;
180 }
181
182 public static int nonTerminating() {
183 int failures = 0;
184 int[] primes = {1, 3, 7, 13, 17};
185
186 // For each pair of prime products, verify the ratio of
187 // non-equal products has a non-terminating expansion.
188
189 for(int i = 0; i < primes.length; i++) {
190 for(int j = i+1; j < primes.length; j++) {
191
192 for(int m = 0; m < primes.length; m++) {
193 for(int n = m+1; n < primes.length; n++) {
194 int dividend = primes[i] * primes[j];
195 int divisor = primes[m] * primes[n];
196
197 if ( ((dividend/divisor) * divisor) != dividend ) {
198 try {
199 BigDecimal quotient = (new BigDecimal(dividend).
200 divide(new BigDecimal(divisor)));
201 failures++;
202 System.err.println("Exact quotient " + quotient.toString() +
203 " returned for non-terminating fraction " +
204 dividend + " / " + divisor + ".");
205 }
206 catch (ArithmeticException e) {
207 ; // Correct result
208 }
209 }
210
211 }
212 }
213 }
214 }
215
216 return failures;
217 }
218
219 public static int properScaleTests(){
220 int failures = 0;
221
222 BigDecimal[][] testCases = {
223 {new BigDecimal("1"), new BigDecimal("5"), new BigDecimal("2e-1")},
224 {new BigDecimal("1"), new BigDecimal("50e-1"), new BigDecimal("2e-1")},
225 {new BigDecimal("10e-1"), new BigDecimal("5"), new BigDecimal("2e-1")},
226 {new BigDecimal("1"), new BigDecimal("500e-2"), new BigDecimal("2e-1")},
227 {new BigDecimal("100e-2"), new BigDecimal("5"), new BigDecimal("20e-2")},
228 {new BigDecimal("1"), new BigDecimal("32"), new BigDecimal("3125e-5")},
229 {new BigDecimal("1"), new BigDecimal("64"), new BigDecimal("15625e-6")},
230 {new BigDecimal("1.0000000"), new BigDecimal("64"), new BigDecimal("156250e-7")},
231 };
232
233
234 for(BigDecimal[] tc : testCases) {
235 BigDecimal quotient;
236 if (! (quotient = tc[0].divide(tc[1])).equals(tc[2]) ) {
237 failures++;
238 System.err.println("Unexpected quotient from " + tc[0] + " / " + tc[1] +
239 "; expected " + tc[2] + " got " + quotient);
240 }
241 }
242
243 return failures;
244 }
245
246 public static int trailingZeroTests() {
247 int failures = 0;
248
249 MathContext mc = new MathContext(3, RoundingMode.FLOOR);
250 BigDecimal[][] testCases = {
251 {new BigDecimal("19"), new BigDecimal("100"), new BigDecimal("0.19")},
252 {new BigDecimal("21"), new BigDecimal("110"), new BigDecimal("0.190")},
253 };
254
255 for(BigDecimal[] tc : testCases) {
256 BigDecimal quotient;
257 if (! (quotient = tc[0].divide(tc[1], mc)).equals(tc[2]) ) {
258 failures++;
259 System.err.println("Unexpected quotient from " + tc[0] + " / " + tc[1] +
260 "; expected " + tc[2] + " got " + quotient);
261 }
262 }
263
264 return failures;
265 }
266
267 public static int scaledRoundedDivideTests() {
268 int failures = 0;
269 // Tests of the traditional scaled divide under different
270 // rounding modes.
271
272 // Encode rounding mode and scale for the divide in a
273 // BigDecimal with the significand equal to the rounding mode
274 // and the scale equal to the number's scale.
275
276 // {dividend, dividisor, rounding, quotient}
277 BigDecimal a = new BigDecimal("31415");
278 BigDecimal a_minus = a.negate();
279 BigDecimal b = new BigDecimal("10000");
280
281 BigDecimal c = new BigDecimal("31425");
282 BigDecimal c_minus = c.negate();
283
284 BigDecimal[][] testCases = {
285 {a, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("3.142")},
286 {a_minus, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("-3.142")},
287
288 {a, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("3.141")},
289 {a_minus, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("-3.141")},
290
291 {a, b, BigDecimal.valueOf(ROUND_CEILING, 3), new BigDecimal("3.142")},
292 {a_minus, b, BigDecimal.valueOf(ROUND_CEILING, 3), new BigDecimal("-3.141")},
293
294 {a, b, BigDecimal.valueOf(ROUND_FLOOR, 3), new BigDecimal("3.141")},
295 {a_minus, b, BigDecimal.valueOf(ROUND_FLOOR, 3), new BigDecimal("-3.142")},
296
297 {a, b, BigDecimal.valueOf(ROUND_HALF_UP, 3), new BigDecimal("3.142")},
298 {a_minus, b, BigDecimal.valueOf(ROUND_HALF_UP, 3), new BigDecimal("-3.142")},
299
300 {a, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("3.141")},
301 {a_minus, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("-3.141")},
302
303 {a, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("3.142")},
304 {a_minus, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("-3.142")},
305
306 {c, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("3.142")},
307 {c_minus, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("-3.142")},
308 };
309
310 for(BigDecimal tc[] : testCases) {
311 int scale = tc[2].scale();
312 int rm = tc[2].unscaledValue().intValue();
313
314 BigDecimal quotient = tc[0].divide(tc[1], scale, rm);
315 if (!quotient.equals(tc[3])) {
316 failures++;
317 System.err.println("Unexpected quotient from " + tc[0] + " / " + tc[1] +
318 " scale " + scale + " rounding mode " + RoundingMode.valueOf(rm) +
319 "; expected " + tc[3] + " got " + quotient);
320 }
321 }
322
323 return failures;
324 }
325
326 public static void main(String argv[]) {
327 int failures = 0;
328
329 failures += powersOf2and5();
330 failures += nonTerminating();
331 failures += properScaleTests();
332 failures += trailingZeroTests();
333 failures += scaledRoundedDivideTests();
334
335 if (failures > 0) {
336 throw new RuntimeException("Incurred " + failures +
337 " failures while testing exact divide.");
338 }
339 }
340}