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 }