1 /*
   2  * Copyright (c) 2003, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4902952 4905407 4916149
  27  * @summary Tests that the scale of zero is propagated properly and has the proper effect.
  28  * @author Joseph D. Darcy
  29  */
  30 
  31 import java.math.*;
  32 import java.util.*;
  33 
  34 public class ZeroScalingTests {
  35 
  36     static MathContext longEnough = new MathContext(50, RoundingMode.UNNECESSARY);
  37 
  38     static BigDecimal[]  zeros = new BigDecimal[23];
  39     static {
  40         for(int i = 0; i < 21; i++) {
  41             zeros[i] = new BigDecimal(BigInteger.ZERO, i-10);
  42         }
  43         zeros[21] = new BigDecimal(BigInteger.ZERO, Integer.MIN_VALUE);
  44         zeros[22] = new BigDecimal(BigInteger.ZERO, Integer.MAX_VALUE);
  45     }
  46 
  47     static BigDecimal element = BigDecimal.valueOf(100, -2);
  48 
  49     static MathContext contexts[] = {
  50         new MathContext(0, RoundingMode.UNNECESSARY),
  51         new MathContext(100, RoundingMode.UNNECESSARY),
  52         new MathContext(5, RoundingMode.UNNECESSARY),
  53         new MathContext(4, RoundingMode.UNNECESSARY),
  54         new MathContext(3, RoundingMode.UNNECESSARY),
  55         new MathContext(2, RoundingMode.UNNECESSARY),
  56         new MathContext(1, RoundingMode.UNNECESSARY),
  57     };
  58 
  59 
  60     static int addTests() {
  61         int failures = 0;
  62 
  63         for(BigDecimal zero1: zeros) {
  64             for(BigDecimal zero2: zeros) {
  65                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
  66                                                      Math.max(zero1.scale(), zero2.scale()));
  67                 BigDecimal result;
  68 
  69                 if(! (result=zero1.add(zero2)).equals(expected) ) {
  70                     failures++;
  71                     System.err.println("For classic exact add, expected scale of " +
  72                                        expected.scale() + "; got " +
  73                                        result.scale() + ".");
  74                 }
  75 
  76                 if(! (result=zero1.add(zero2, MathContext.UNLIMITED)).equals(expected) ) {
  77                     failures++;
  78                     System.err.println("For UNLIMITED math context add," +
  79                                        " expected scale of " +
  80                                        expected.scale() + "; got " +
  81                                        result.scale() + ".");
  82                 }
  83 
  84                 if(! (result=zero1.add(zero2, longEnough)).equals(expected) ) {
  85                     failures++;
  86                     System.err.println("For longEnough math context add," +
  87                                        " expected scale of " +
  88                                        expected.scale() + "; got " +
  89                                        result.scale() + ".");
  90                 }
  91 
  92             }
  93         }
  94 
  95         // Test effect of adding zero to a nonzero value.
  96         for (MathContext mc: contexts) {
  97             for (BigDecimal zero: zeros) {
  98                 if (Math.abs((long)zero.scale()) < 100 ) {
  99 
 100                     int preferredScale = Math.max(zero.scale(), element.scale());
 101                     if (mc.getPrecision() != 0) {
 102                         if (preferredScale < -4 )
 103                             preferredScale = -4;
 104                         else if (preferredScale > -(5 - mc.getPrecision())) {
 105                             preferredScale = -(5 - mc.getPrecision());
 106                         }
 107                     }
 108 
 109 
 110                     /*
 111                       System.err.println("\n    " + element + " +\t" + zero + " =\t" + result);
 112 
 113                       System.err.println("scales" + element.scale() + " \t" + zero.scale() +
 114                       "  \t " + result.scale() + "\t precison = " + mc.getPrecision());
 115                       System.err.println("expected scale = " + preferredScale);
 116                     */
 117 
 118                     BigDecimal result = element.add(zero, mc);
 119                     if (result.scale() != preferredScale ||
 120                             result.compareTo(element) != 0) {
 121                         failures++;
 122                         System.err.println("Expected scale  " + preferredScale +
 123                                            " result scale was " + result.scale() +
 124                                            " ; value was " + result);
 125                     }
 126 
 127                     result = zero.add(element, mc);
 128                     if (result.scale() != preferredScale ||
 129                             result.compareTo(element) != 0) {
 130                         failures++;
 131                         System.err.println("Expected scale  " + preferredScale +
 132                                            " result scale was " + result.scale() +
 133                                            " ; value was " + result);
 134                     }
 135 
 136                     result = element.negate().add(zero, mc);
 137                     if (result.scale() != preferredScale ||
 138                             result.compareTo(element.negate()) != 0) {
 139                         failures++;
 140                         System.err.println("Expected scale  " + preferredScale +
 141                                            " result scale was " + result.scale() +
 142                                            " ; value was " + result);
 143                     }
 144 
 145                     result = zero.add(element.negate(), mc);
 146                     if (result.scale() != preferredScale ||
 147                             result.compareTo(element.negate()) != 0) {
 148                         failures++;
 149                         System.err.println("Expected scale  " + preferredScale +
 150                                            " result scale was " + result.scale() +
 151                                            " ; value was " + result);
 152                     }
 153 
 154                 }
 155             }
 156         }
 157 
 158         return failures;
 159     }
 160 
 161     static int subtractTests() {
 162         int failures = 0;
 163 
 164         for(BigDecimal zero1: zeros) {
 165             for(BigDecimal zero2: zeros) {
 166                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
 167                                                      Math.max(zero1.scale(), zero2.scale()));
 168                 BigDecimal result;
 169 
 170                 if(! (result=zero1.subtract(zero2)).equals(expected) ) {
 171                     failures++;
 172                     System.err.println("For classic exact subtract, expected scale of " +
 173                                        expected.scale() + "; got " +
 174                                        result.scale() + ".");
 175                 }
 176 
 177                 if(! (result=zero1.subtract(zero2, MathContext.UNLIMITED)).equals(expected) ) {
 178                     failures++;
 179                     System.err.println("For UNLIMITED math context subtract," +
 180                                        " expected scale of " +
 181                                        expected.scale() + "; got " +
 182                                        result.scale() + ".");
 183                 }
 184 
 185                 if(! (result=zero1.subtract(zero2, longEnough)).equals(expected) ) {
 186                     failures++;
 187                     System.err.println("For longEnough math context subtract," +
 188                                        " expected scale of " +
 189                                        expected.scale() + "; got " +
 190                                        result.scale() + ".");
 191                 }
 192 
 193             }
 194         }
 195 
 196 
 197         // Test effect of adding zero to a nonzero value.
 198         for (MathContext mc: contexts) {
 199             for (BigDecimal zero: zeros) {
 200                 if (Math.abs((long)zero.scale()) < 100 ) {
 201 
 202                     int preferredScale = Math.max(zero.scale(), element.scale());
 203                     if (mc.getPrecision() != 0) {
 204                         if (preferredScale < -4 )
 205                             preferredScale = -4;
 206                         else if (preferredScale > -(5 - mc.getPrecision())) {
 207                             preferredScale = -(5 - mc.getPrecision());
 208                         }
 209                     }
 210 
 211 
 212                     /*
 213                       System.err.println("\n    " + element + " +\t" + zero + " =\t" + result);
 214 
 215                       System.err.println("scales" + element.scale() + " \t" + zero.scale() +
 216                       "  \t " + result.scale() + "\t precison = " + mc.getPrecision());
 217                       System.err.println("expected scale = " + preferredScale);
 218                     */
 219 
 220                     BigDecimal result = element.subtract(zero, mc);
 221                     if (result.scale() != preferredScale ||
 222                             result.compareTo(element) != 0) {
 223                         failures++;
 224                         System.err.println("Expected scale  " + preferredScale +
 225                                            " result scale was " + result.scale() +
 226                                            " ; value was " + result);
 227                     }
 228 
 229                     result = zero.subtract(element, mc);
 230                     if (result.scale() != preferredScale ||
 231                             result.compareTo(element.negate()) != 0) {
 232                         failures++;
 233                         System.err.println("Expected scale  " + preferredScale +
 234                                            " result scale was " + result.scale() +
 235                                            " ; value was " + result);
 236                     }
 237 
 238                     result = element.negate().subtract(zero, mc);
 239                     if (result.scale() != preferredScale ||
 240                             result.compareTo(element.negate()) != 0) {
 241                         failures++;
 242                         System.err.println("Expected scale  " + preferredScale +
 243                                            " result scale was " + result.scale() +
 244                                            " ; value was " + result);
 245                     }
 246 
 247                     result = zero.subtract(element.negate(), mc);
 248                     if (result.scale() != preferredScale ||
 249                             result.compareTo(element) != 0) {
 250                         failures++;
 251                         System.err.println("Expected scale  " + preferredScale +
 252                                            " result scale was " + result.scale() +
 253                                            " ; value was " + result);
 254                     }
 255 
 256                 }
 257             }
 258         }
 259 
 260         return failures;
 261     }
 262 
 263     static int multiplyTests() {
 264         int failures = 0;
 265 
 266         BigDecimal ones[] = {
 267             BigDecimal.valueOf(1, 0),
 268             BigDecimal.valueOf(10, 1),
 269             BigDecimal.valueOf(1000, 3),
 270             BigDecimal.valueOf(100000000, 8),
 271         };
 272 
 273         List<BigDecimal> values = new LinkedList<BigDecimal>();
 274         values.addAll(Arrays.asList(zeros));
 275         values.addAll(Arrays.asList(ones));
 276 
 277         for(BigDecimal zero1: zeros) {
 278             for(BigDecimal value: values) {
 279                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
 280                                                      (int)Math.min(Math.max((long)zero1.scale()+value.scale(),
 281                                                                             Integer.MIN_VALUE ),
 282                                                                    Integer.MAX_VALUE ) );
 283                 BigDecimal result;
 284 
 285                 if(! (result=zero1.multiply(value)).equals(expected) ) {
 286                     failures++;
 287                     System.err.println("For classic exact multiply, expected scale of " +
 288                                        expected.scale() + "; got " +
 289                                        result.scale() + ".");
 290                 }
 291 
 292                 if(! (result=zero1.multiply(value, MathContext.UNLIMITED)).equals(expected) ) {
 293                     failures++;
 294                     System.err.println("For UNLIMITED math context multiply," +
 295                                        " expected scale of " +
 296                                        expected.scale() + "; got " +
 297                                        result.scale() + ".");
 298                 }
 299 
 300                 if(! (result=zero1.multiply(value, longEnough)).equals(expected) ) {
 301                     failures++;
 302                     System.err.println("For longEnough math context multiply," +
 303                                        " expected scale of " +
 304                                        expected.scale() + "; got " +
 305                                        result.scale() + ".");
 306                 }
 307 
 308             }
 309         }
 310 
 311         return failures;
 312     }
 313 
 314     static int divideTests() {
 315         int failures = 0;
 316 
 317         BigDecimal [] ones = {
 318             BigDecimal.valueOf(1, 0),
 319             BigDecimal.valueOf(10, -1),
 320             BigDecimal.valueOf(100, -2),
 321             BigDecimal.valueOf(1000, -3),
 322             BigDecimal.valueOf(1000000, -5),
 323         };
 324 
 325         for(BigDecimal one: ones) {
 326             for(BigDecimal zero: zeros) {
 327                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
 328                                                      (int)Math.min(Math.max((long)zero.scale() - one.scale(),
 329                                                                             Integer.MIN_VALUE ),
 330                                                                    Integer.MAX_VALUE ) );
 331                 BigDecimal result;
 332 
 333                 if(! (result=zero.divide(one)).equals(expected) ) {
 334                     failures++;
 335                     System.err.println("For classic exact divide, expected scale of " +
 336                                        expected.scale() + "; got " +
 337                                        result.scale() + ".");
 338                 }
 339 
 340                 if(! (result=zero.divide(one, MathContext.UNLIMITED)).equals(expected) ) {
 341                     failures++;
 342                     System.err.println("For UNLIMITED math context divide," +
 343                                        " expected scale of " +
 344                                        expected.scale() + "; got " +
 345                                        result.scale() + ".");
 346                 }
 347 
 348                 if(! (result=zero.divide(one, longEnough)).equals(expected) ) {
 349                     failures++;
 350                     System.err.println("For longEnough math context divide," +
 351                                        " expected scale of " +
 352                                        expected.scale() + "; got " +
 353                                        result.scale() + ".");
 354                 }
 355 
 356             }
 357         }
 358 
 359         return failures;
 360     }
 361 
 362     static int setScaleTests() {
 363         int failures = 0;
 364 
 365         int scales[] = {
 366             Integer.MIN_VALUE,
 367             Integer.MIN_VALUE+1,
 368             -10000000,
 369             -3,
 370             -2,
 371             -1,
 372             0,
 373             1,
 374             2,
 375             3,
 376             10,
 377             10000000,
 378             Integer.MAX_VALUE-1,
 379             Integer.MAX_VALUE
 380         };
 381 
 382         for(BigDecimal zero: zeros) {
 383             for(int scale: scales) {
 384                 try {
 385                     BigDecimal bd = zero.setScale(scale);
 386                 }
 387                 catch (ArithmeticException e) {
 388                     failures++;
 389                     System.err.println("Exception when trying to set a scale of " + scale +
 390                                        " on " + zero);
 391                 }
 392             }
 393         }
 394 
 395         return failures;
 396     }
 397 
 398     static int toEngineeringStringTests() {
 399         int failures = 0;
 400 
 401         String [][] testCases  = {
 402             {"0E+10",   "0.00E+12"},
 403             {"0E+9",    "0E+9"},
 404             {"0E+8",    "0.0E+9"},
 405             {"0E+7",    "0.00E+9"},
 406 
 407             {"0E-10",   "0.0E-9"},
 408             {"0E-9",    "0E-9"},
 409             {"0E-8",    "0.00E-6"},
 410             {"0E-7",    "0.0E-6"},
 411         };
 412 
 413         for(String[] testCase: testCases) {
 414             BigDecimal bd = new BigDecimal(testCase[0]);
 415             String result = bd.toEngineeringString();
 416 
 417             if (!result.equals(testCase[1]) ||
 418                 !bd.equals(new BigDecimal(result))) {
 419                 failures++;
 420                 System.err.println("From input ``" + testCase[0] + ",'' " +
 421                                    " bad engineering string output ``" + result +
 422                                    "''; expected ``" + testCase[1] + ".''");
 423             }
 424 
 425         }
 426 
 427         return failures;
 428     }
 429 
 430     static int ulpTests() {
 431         int failures = 0;
 432 
 433         for(BigDecimal zero: zeros) {
 434             BigDecimal result;
 435             BigDecimal expected = BigDecimal.valueOf(1, zero.scale());
 436 
 437             if (! (result=zero.ulp()).equals(expected) ) {
 438                 failures++;
 439                 System.err.println("Unexpected ulp value for zero value " +
 440                                    zero + "; expected " + expected +
 441                                    ", got " + result);
 442             }
 443         }
 444 
 445         return failures;
 446     }
 447 
 448     public static void main(String argv[]) {
 449         int failures = 0;
 450 
 451         failures += addTests();
 452         failures += subtractTests();
 453         failures += multiplyTests();
 454         failures += divideTests();
 455         failures += setScaleTests();
 456         failures += toEngineeringStringTests();
 457         failures += ulpTests();
 458 
 459         if (failures > 0 ) {
 460             throw new RuntimeException("Incurred " + failures + " failures" +
 461                                        " testing the preservation of zero scales.");
 462         }
 463     }
 464 }