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 }