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