darcy | 32db449 | 2009-01-26 19:49:26 -0800 | [diff] [blame^] | 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 |
| 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 | 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 | } |