1 /* 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 6876282 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 33 import java.math.*; 34 import static java.math.BigDecimal.*; 35 36 public 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 // Ad hoc tests 285 BigDecimal d = new BigDecimal(new BigInteger("-37361671119238118911893939591735"), 10); 286 BigDecimal e = new BigDecimal(new BigInteger("74723342238476237823787879183470"), 15); 287 288 BigDecimal[][] testCases = { 289 {a, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("3.142")}, 290 {a_minus, b, BigDecimal.valueOf(ROUND_UP, 3), new BigDecimal("-3.142")}, 291 292 {a, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("3.141")}, 293 {a_minus, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("-3.141")}, 294 295 {a, b, BigDecimal.valueOf(ROUND_CEILING, 3), new BigDecimal("3.142")}, 296 {a_minus, b, BigDecimal.valueOf(ROUND_CEILING, 3), new BigDecimal("-3.141")}, 297 298 {a, b, BigDecimal.valueOf(ROUND_FLOOR, 3), new BigDecimal("3.141")}, 299 {a_minus, b, BigDecimal.valueOf(ROUND_FLOOR, 3), new BigDecimal("-3.142")}, 300 301 {a, b, BigDecimal.valueOf(ROUND_HALF_UP, 3), new BigDecimal("3.142")}, 302 {a_minus, b, BigDecimal.valueOf(ROUND_HALF_UP, 3), new BigDecimal("-3.142")}, 303 304 {a, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("3.141")}, 305 {a_minus, b, BigDecimal.valueOf(ROUND_DOWN, 3), new BigDecimal("-3.141")}, 306 307 {a, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("3.142")}, 308 {a_minus, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("-3.142")}, 309 310 {c, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("3.142")}, 311 {c_minus, b, BigDecimal.valueOf(ROUND_HALF_EVEN, 3), new BigDecimal("-3.142")}, 312 313 {d, e, BigDecimal.valueOf(ROUND_HALF_UP, -5), BigDecimal.valueOf(-1, -5)}, 314 {d, e, BigDecimal.valueOf(ROUND_HALF_DOWN, -5), BigDecimal.valueOf(0, -5)}, 315 {d, e, BigDecimal.valueOf(ROUND_HALF_EVEN, -5), BigDecimal.valueOf(0, -5)}, 316 }; 317 318 for(BigDecimal tc[] : testCases) { 319 int scale = tc[2].scale(); 320 int rm = tc[2].unscaledValue().intValue(); 321 322 BigDecimal quotient = tc[0].divide(tc[1], scale, rm); 323 if (!quotient.equals(tc[3])) { 324 failures++; 325 System.err.println("Unexpected quotient from " + tc[0] + " / " + tc[1] + 326 " scale " + scale + " rounding mode " + RoundingMode.valueOf(rm) + 327 "; expected " + tc[3] + " got " + quotient); 328 } 329 } 330 331 // 6876282 332 BigDecimal[][] testCases2 = { 333 // { dividend, divisor, expected quotient } 334 { new BigDecimal(3090), new BigDecimal(7), new BigDecimal(441) }, 335 { new BigDecimal("309000000000000000000000"), new BigDecimal("700000000000000000000"), 336 new BigDecimal(441) }, 337 { new BigDecimal("962.430000000000"), new BigDecimal("8346463.460000000000"), 338 new BigDecimal("0.000115309916") }, 339 { new BigDecimal("18446744073709551631"), new BigDecimal("4611686018427387909"), 340 new BigDecimal(4) }, 341 { new BigDecimal("18446744073709551630"), new BigDecimal("4611686018427387909"), 342 new BigDecimal(4) }, 343 { new BigDecimal("23058430092136939523"), new BigDecimal("4611686018427387905"), 344 new BigDecimal(5) }, 345 { new BigDecimal("-18446744073709551661"), new BigDecimal("-4611686018427387919"), 346 new BigDecimal(4) }, 347 { new BigDecimal("-18446744073709551660"), new BigDecimal("-4611686018427387919"), 348 new BigDecimal(4) }, 349 }; 350 351 for (BigDecimal test[] : testCases2) { 352 BigDecimal quo = test[0].divide(test[1], RoundingMode.HALF_UP); 353 if (!quo.equals(test[2])) { 354 failures++; 355 System.err.println("Unexpected quotient from " + test[0] + " / " + test[1] + 356 " rounding mode HALF_UP" + 357 "; expected " + test[2] + " got " + quo); 358 } 359 } 360 return failures; 361 } 362 363 public static void main(String argv[]) { 364 int failures = 0; 365 366 failures += powersOf2and5(); 367 failures += nonTerminating(); 368 failures += properScaleTests(); 369 failures += trailingZeroTests(); 370 failures += scaledRoundedDivideTests(); 371 372 if (failures > 0) { 373 throw new RuntimeException("Incurred " + failures + 374 " failures while testing exact divide."); 375 } 376 } 377 }