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