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 }