1 /*
   2  * Copyright 2018-2019 Raffaello Giulietti
   3  *
   4  * Permission is hereby granted, free of charge, to any person obtaining a copy
   5  * of this software and associated documentation files (the "Software"), to deal
   6  * in the Software without restriction, including without limitation the rights
   7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8  * copies of the Software, and to permit persons to whom the Software is
   9  * furnished to do so, subject to the following conditions:
  10  *
  11  * The above copyright notice and this permission notice shall be included in
  12  * all copies or substantial portions of the Software.
  13  *
  14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20  * THE SOFTWARE.
  21  */
  22 
  23 package jdk.internal.math;
  24 
  25 import java.io.IOException;
  26 import java.io.StringReader;
  27 import java.math.BigDecimal;
  28 import java.math.BigInteger;
  29 
  30 /*
  31 A checker for the Javadoc specification.
  32 It just relies on straightforward use of (expensive) BigDecimal arithmetic,
  33 not optimized at all.
  34  */
  35 abstract class ToDecimalChecker extends BasicChecker {
  36 
  37     // The string to check
  38     private final String s;
  39 
  40     // The decimal parsed from s is c 10^q
  41     private long c;
  42     private int q;
  43 
  44     // The number of digits parsed from s: 10^(len10-1) <= c < 10^len10
  45     private int len10;
  46 
  47     ToDecimalChecker(String s) {
  48         this.s = s;
  49     }
  50 
  51     /*
  52     Returns e be such that 10^(e-1) <= c 2^q < 10^e.
  53      */
  54     static int e(double v) {
  55         // log10(v) + 1 is a first good approximation of e
  56         int e = (int) Math.floor(Math.log10(v)) + 1;
  57 
  58         // Full precision search for e such that 10^(e-1) <= c 2^q < 10^e.
  59         BigDecimal bv = new BigDecimal(v);
  60         BigDecimal low = new BigDecimal(BigInteger.ONE, -(e - 1));
  61         while (low.compareTo(bv) > 0) {
  62             e -= 1;
  63             low = new BigDecimal(BigInteger.ONE, -(e - 1));
  64         }
  65         BigDecimal high = new BigDecimal(BigInteger.ONE, -e);
  66         while (bv.compareTo(high) >= 0) {
  67             e += 1;
  68             high = new BigDecimal(BigInteger.ONE, -e);
  69         }
  70         return e;
  71     }
  72 
  73     void assertTrue() {
  74         if (isOK()) {
  75             return;
  76         }
  77         String msg = "toString applied to the bits " +
  78                 hexBits() +
  79                 " returns " +
  80                 "\"" + s + "\"" +
  81                 ", which is not correct according to the specification.";
  82         if (FAILURE_THROWS_EXCEPTION) {
  83             throw new RuntimeException(msg);
  84         }
  85         System.err.println(msg);
  86     }
  87 
  88     /*
  89     Returns whether s syntactically meets the expected output of
  90     toString. It is restricted to finite positive outputs.
  91     It is an unusually long method but rather straightforward, too.
  92     Many conditionals could be merged, but KISS here.
  93      */
  94     private boolean parse(String t) {
  95         try {
  96             // first determine interesting boundaries in the string
  97             StringReader r = new StringReader(t);
  98             int ch = r.read();
  99 
 100             int i = 0;
 101             while (ch == '0') {
 102                 ++i;
 103                 ch = r.read();
 104             }
 105             // i is just after zeroes starting the integer
 106 
 107             int p = i;
 108             while ('0' <= ch && ch <= '9') {
 109                 c = 10 * c + (ch - '0');
 110                 if (c < 0) {
 111                     return false;
 112                 }
 113                 ++len10;
 114                 ++p;
 115                 ch = r.read();
 116             }
 117             // p is just after digits ending the integer
 118 
 119             int fz = p;
 120             if (ch == '.') {
 121                 ++fz;
 122                 ch = r.read();
 123             }
 124             // fz is just after a decimal '.'
 125 
 126             int f = fz;
 127             while (ch == '0') {
 128                 c = 10 * c + (ch - '0');
 129                 if (c < 0) {
 130                     return false;
 131                 }
 132                 ++len10;
 133                 ++f;
 134                 ch = r.read();
 135             }
 136             // f is just after zeroes starting the fraction
 137 
 138             if (c == 0) {
 139                 len10 = 0;
 140             }
 141             int x = f;
 142             while ('0' <= ch && ch <= '9') {
 143                 c = 10 * c + (ch - '0');
 144                 if (c < 0) {
 145                     return false;
 146                 }
 147                 ++len10;
 148                 ++x;
 149                 ch = r.read();
 150             }
 151             // x is just after digits ending the fraction
 152 
 153             int g = x;
 154             if (ch == 'E') {
 155                 ++g;
 156                 ch = r.read();
 157             }
 158             // g is just after an exponent indicator 'E'
 159 
 160             int ez = g;
 161             if (ch == '-') {
 162                 ++ez;
 163                 ch = r.read();
 164             }
 165             // ez is just after a '-' sign in the exponent
 166 
 167             int e = ez;
 168             while (ch == '0') {
 169                 ++e;
 170                 ch = r.read();
 171             }
 172             // e is just after zeroes starting the exponent
 173 
 174             int z = e;
 175             while ('0' <= ch && ch <= '9') {
 176                 q = 10 * q + (ch - '0');
 177                 if (q < 0) {
 178                     return false;
 179                 }
 180                 ++z;
 181                 ch = r.read();
 182             }
 183             // z is just after digits ending the exponent
 184 
 185             // No other char after the number
 186             if (z != t.length()) {
 187                 return false;
 188             }
 189 
 190             // The integer must be present
 191             if (p == 0) {
 192                 return false;
 193             }
 194 
 195             // The decimal '.' must be present
 196             if (fz == p) {
 197                 return false;
 198             }
 199 
 200             // The fraction must be present
 201             if (x == fz) {
 202                 return false;
 203             }
 204 
 205             // The fraction is not 0 or it consists of exactly one 0
 206             if (f == x && f - fz > 1) {
 207                 return false;
 208             }
 209 
 210             // Plain notation, no exponent
 211             if (x == z) {
 212                 // At most one 0 starting the integer
 213                 if (i > 1) {
 214                     return false;
 215                 }
 216 
 217                 // If the integer is 0, at most 2 zeroes start the fraction
 218                 if (i == 1 && f - fz > 2) {
 219                     return false;
 220                 }
 221 
 222                 // The integer cannot have more than 7 digits
 223                 if (p > 7) {
 224                     return false;
 225                 }
 226 
 227                 q = fz - x;
 228 
 229                 // OK for plain notation
 230                 return true;
 231             }
 232 
 233             // Computerized scientific notation
 234 
 235             // The integer has exactly one nonzero digit
 236             if (i != 0 || p != 1) {
 237                 return false;
 238             }
 239 
 240             //
 241             // There must be an exponent indicator
 242             if (x == g) {
 243                 return false;
 244             }
 245 
 246             // There must be an exponent
 247             if (ez == z) {
 248                 return false;
 249             }
 250 
 251             // The exponent must not start with zeroes
 252             if (ez != e) {
 253                 return false;
 254             }
 255 
 256             if (g != ez) {
 257                 q = -q;
 258             }
 259 
 260             // The exponent must not lie in [-3, 7)
 261             if (-3 <= q && q < 7) {
 262                 return false;
 263             }
 264 
 265             q += fz - x;
 266 
 267             // OK for computerized scientific notation
 268             return true;
 269         } catch (IOException ex) {
 270             // An IOException on a StringReader??? Please...
 271             return false;
 272         }
 273     }
 274 
 275     private boolean isOK() {
 276         if (isNaN()) {
 277             return s.equals("NaN");
 278         }
 279         String t = s;
 280         if (isNegative()) {
 281             if (s.isEmpty() || s.charAt(0) != '-') {
 282                 return false;
 283             }
 284             negate();
 285             t = s.substring(1);
 286         }
 287         if (isInfinity()) {
 288             return t.equals("Infinity");
 289         }
 290         if (isZero()) {
 291             return t.equals("0.0");
 292         }
 293         if (!parse(t)) {
 294             return false;
 295         }
 296         if (len10 < 2) {
 297             c *= 10;
 298             q -= 1;
 299             len10 += 1;
 300         }
 301         if (2 > len10 || len10 > maxLen10()) {
 302             return false;
 303         }
 304 
 305         // The exponent is bounded
 306         if (minExp() > q + len10 || q + len10 > maxExp()) {
 307             return false;
 308         }
 309 
 310         // s must recover v
 311         try {
 312             if (!recovers(t)) {
 313                 return false;
 314             }
 315         } catch (NumberFormatException e) {
 316             return false;
 317         }
 318 
 319         // Get rid of trailing zeroes, still ensuring at least 2 digits
 320         while (len10 > 2 && c % 10 == 0) {
 321             c /= 10;
 322             q += 1;
 323             len10 -= 1;
 324         }
 325 
 326         if (len10 > 2) {
 327             // Try with a shorter number less than v...
 328             if (recovers(BigDecimal.valueOf(c / 10, -q - 1))) {
 329                 return false;
 330             }
 331 
 332             // ... and with a shorter number greater than v
 333             if (recovers(BigDecimal.valueOf(c / 10 + 1, -q - 1))) {
 334                 return false;
 335             }
 336         }
 337 
 338         // Try with the decimal predecessor...
 339         BigDecimal dp = c == 10 ?
 340                 BigDecimal.valueOf(99, -q + 1) :
 341                 BigDecimal.valueOf(c - 1, -q);
 342         if (recovers(dp)) {
 343             BigDecimal bv = toBigDecimal();
 344             BigDecimal deltav = bv.subtract(BigDecimal.valueOf(c, -q));
 345             if (deltav.signum() >= 0) {
 346                 return true;
 347             }
 348             BigDecimal delta = dp.subtract(bv);
 349             if (delta.signum() >= 0) {
 350                 return false;
 351             }
 352             int cmp = deltav.compareTo(delta);
 353             return cmp > 0 || cmp == 0 && (c & 0x1) == 0;
 354         }
 355 
 356         // ... and with the decimal successor
 357         BigDecimal ds = BigDecimal.valueOf(c + 1, -q);
 358         if (recovers(ds)) {
 359             BigDecimal bv = toBigDecimal();
 360             BigDecimal deltav = bv.subtract(BigDecimal.valueOf(c, -q));
 361             if (deltav.signum() <= 0) {
 362                 return true;
 363             }
 364             BigDecimal delta = ds.subtract(bv);
 365             if (delta.signum() <= 0) {
 366                 return false;
 367             }
 368             int cmp = deltav.compareTo(delta);
 369             return cmp < 0 || cmp == 0 && (c & 0x1) == 0;
 370         }
 371 
 372         return true;
 373     }
 374 
 375     abstract BigDecimal toBigDecimal();
 376 
 377     abstract boolean recovers(BigDecimal b);
 378 
 379     abstract boolean recovers(String s);
 380 
 381     abstract String hexBits();
 382 
 383     abstract int minExp();
 384 
 385     abstract int maxExp();
 386 
 387     abstract int maxLen10();
 388 
 389     abstract boolean isZero();
 390 
 391     abstract boolean isInfinity();
 392 
 393     abstract void negate();
 394 
 395     abstract boolean isNegative();
 396 
 397     abstract boolean isNaN();
 398 
 399 }