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