1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package java.lang; 19 20 /** 21 * Used to parse a string and return either a single or double precision 22 * floating point number. 23 * @hide 24 */ 25 final class StringToReal { 26 27 private static final class StringExponentPair { 28 String s; 29 long e; 30 boolean negative; 31 32 // Flags for two special non-error failure cases. 33 boolean infinity; 34 boolean zero; 35 36 public float specialValue() { 37 if (infinity) { 38 return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; 39 } 40 return negative ? -0.0f : 0.0f; 41 } 42 } 43 44 /** 45 * Takes a String and an integer exponent. The String should hold a positive 46 * integer value (or zero). The exponent will be used to calculate the 47 * floating point number by taking the positive integer the String 48 * represents and multiplying by 10 raised to the power of the of the 49 * exponent. Returns the closest double value to the real number, or Double.longBitsToDouble(-1). 50 */ 51 private static native double parseDblImpl(String s, int e); 52 53 /** 54 * Takes a String and an integer exponent. The String should hold a positive 55 * integer value (or zero). The exponent will be used to calculate the 56 * floating point number by taking the positive integer the String 57 * represents and multiplying by 10 raised to the power of the of the 58 * exponent. Returns the closest float value to the real number, or Float.intBitsToFloat(-1). 59 */ 60 private static native float parseFltImpl(String s, int e); 61 62 private static NumberFormatException invalidReal(String s, boolean isDouble) { 63 throw new NumberFormatException("Invalid " + (isDouble ? "double" : "float") + ": \"" + s + "\""); 64 } 65 66 /** 67 * Returns a StringExponentPair containing a String with no leading or trailing white 68 * space and trailing zeroes eliminated. The exponent of the 69 * StringExponentPair will be used to calculate the floating point number by 70 * taking the positive integer the String represents and multiplying by 10 71 * raised to the power of the of the exponent. 72 */ 73 private static StringExponentPair initialParse(String s, int length, boolean isDouble) { 74 StringExponentPair result = new StringExponentPair(); 75 if (length == 0) { 76 throw invalidReal(s, isDouble); 77 } 78 result.negative = (s.charAt(0) == '-'); 79 80 // We ignore trailing double or float indicators; the method you called determines 81 // what you'll get. 82 char c = s.charAt(length - 1); 83 if (c == 'D' || c == 'd' || c == 'F' || c == 'f') { 84 length--; 85 if (length == 0) { 86 throw invalidReal(s, isDouble); 87 } 88 } 89 90 int end = Math.max(s.indexOf('E'), s.indexOf('e')); 91 if (end != -1) { 92 // Is there anything after the 'e'? 93 if (end + 1 == length) { 94 throw invalidReal(s, isDouble); 95 } 96 97 // Do we have an optional explicit sign? 98 int exponentOffset = end + 1; 99 boolean negativeExponent = false; 100 char firstExponentChar = s.charAt(exponentOffset); 101 if (firstExponentChar == '+' || firstExponentChar == '-') { 102 negativeExponent = (firstExponentChar == '-'); 103 ++exponentOffset; 104 } 105 106 // Do we have a valid positive integer? 107 String exponentString = s.substring(exponentOffset, length); 108 if (exponentString.isEmpty()) { 109 throw invalidReal(s, isDouble); 110 } 111 for (int i = 0; i < exponentString.length(); ++i) { 112 char ch = exponentString.charAt(i); 113 if (ch < '0' || ch > '9') { 114 throw invalidReal(s, isDouble); 115 } 116 } 117 118 // Parse the integer exponent. 119 try { 120 result.e = Integer.parseInt(exponentString); 121 if (negativeExponent) { 122 result.e = -result.e; 123 } 124 } catch (NumberFormatException ex) { 125 // We already checked the string, so the exponent must have been out of range for an 126 // int. 127 if (negativeExponent) { 128 result.zero = true; 129 } else { 130 result.infinity = true; 131 } 132 // Fall through: We want to check the content of the mantissa and throw an 133 // exception if it contains invalid characters. For example: "JUNK" * 10^12 should 134 // be treated as an error, not as infinity. 135 } 136 } else { 137 end = length; 138 } 139 140 int start = 0; 141 c = s.charAt(start); 142 if (c == '-') { 143 ++start; 144 --length; 145 result.negative = true; 146 } else if (c == '+') { 147 ++start; 148 --length; 149 } 150 if (length == 0) { 151 throw invalidReal(s, isDouble); 152 } 153 154 // Confirm that the mantissa should parse. 155 int decimal = -1; 156 for (int i = start; i < end; i++) { 157 char mc = s.charAt(i); 158 if (mc == '.') { 159 if (decimal != -1) { 160 throw invalidReal(s, isDouble); 161 } 162 decimal = i; 163 } else if (mc < '0' || mc > '9') { 164 throw invalidReal(s, isDouble); 165 } 166 } 167 if (decimal > -1) { 168 result.e -= end - decimal - 1; 169 s = s.substring(start, decimal) + s.substring(decimal + 1, end); 170 } else { 171 s = s.substring(start, end); 172 } 173 174 length = s.length(); 175 if (length == 0) { 176 throw invalidReal(s, isDouble); 177 } 178 179 // All syntactic checks that might throw an exception are above. If we have established 180 // one of the non-exception error conditions we can stop here. 181 if (result.infinity || result.zero) { 182 return result; 183 } 184 185 end = length; 186 while (end > 1 && s.charAt(end - 1) == '0') { 187 --end; 188 } 189 190 start = 0; 191 while (start < end - 1 && s.charAt(start) == '0') { 192 start++; 193 } 194 195 if (end != length || start != 0) { 196 result.e += length - end; 197 s = s.substring(start, end); 198 } 199 200 // This is a hack for https://issues.apache.org/jira/browse/HARMONY-329 201 // Trim the length of very small numbers, natives can only handle down 202 // to E-309 203 final int APPROX_MIN_MAGNITUDE = -359; 204 final int MAX_DIGITS = 52; 205 length = s.length(); 206 if (length > MAX_DIGITS && result.e < APPROX_MIN_MAGNITUDE) { 207 int d = Math.min(APPROX_MIN_MAGNITUDE - (int) result.e, length - 1); 208 s = s.substring(0, length - d); 209 result.e += d; 210 } 211 212 // This is a hack for https://issues.apache.org/jira/browse/HARMONY-6641 213 // The magic 1024 was determined experimentally; the more plausible -324 and +309 were 214 // not sufficient to pass both our tests and harmony's tests. 215 if (result.e < -1024) { 216 result.zero = true; 217 return result; 218 } else if (result.e > 1024) { 219 result.infinity = true; 220 return result; 221 } 222 223 result.s = s; 224 return result; 225 } 226 227 // Parses "+Nan", "NaN", "-Nan", "+Infinity", "Infinity", and "-Infinity", case-insensitively. 228 private static float parseName(String name, boolean isDouble) { 229 // Explicit sign? 230 boolean negative = false; 231 int i = 0; 232 int length = name.length(); 233 char firstChar = name.charAt(i); 234 if (firstChar == '-') { 235 negative = true; 236 ++i; 237 --length; 238 } else if (firstChar == '+') { 239 ++i; 240 --length; 241 } 242 243 if (length == 8 && name.regionMatches(false, i, "Infinity", 0, 8)) { 244 return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; 245 } 246 if (length == 3 && name.regionMatches(false, i, "NaN", 0, 3)) { 247 return Float.NaN; 248 } 249 throw invalidReal(name, isDouble); 250 } 251 252 /** 253 * Returns the closest double value to the real number in the string. 254 * 255 * @param s 256 * the String that will be parsed to a floating point 257 * @return the double closest to the real number 258 * 259 * @throws NumberFormatException 260 * if the String doesn't represent a double 261 */ 262 public static double parseDouble(String s) { 263 s = s.trim(); 264 int length = s.length(); 265 266 if (length == 0) { 267 throw invalidReal(s, true); 268 } 269 270 // See if this could be a named double 271 char last = s.charAt(length - 1); 272 if (last == 'y' || last == 'N') { 273 return parseName(s, true); 274 } 275 276 // See if it could be a hexadecimal representation. 277 // We don't use startsWith because there might be a leading sign. 278 if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) { 279 return HexStringParser.parseDouble(s); 280 } 281 282 StringExponentPair info = initialParse(s, length, true); 283 if (info.infinity || info.zero) { 284 return info.specialValue(); 285 } 286 double result = parseDblImpl(info.s, (int) info.e); 287 if (Double.doubleToRawLongBits(result) == 0xffffffffffffffffL) { 288 throw invalidReal(s, true); 289 } 290 return info.negative ? -result : result; 291 } 292 293 /** 294 * Returns the closest float value to the real number in the string. 295 * 296 * @param s 297 * the String that will be parsed to a floating point 298 * @return the float closest to the real number 299 * 300 * @throws NumberFormatException 301 * if the String doesn't represent a float 302 */ 303 public static float parseFloat(String s) { 304 s = s.trim(); 305 int length = s.length(); 306 307 if (length == 0) { 308 throw invalidReal(s, false); 309 } 310 311 // See if this could be a named float 312 char last = s.charAt(length - 1); 313 if (last == 'y' || last == 'N') { 314 return parseName(s, false); 315 } 316 317 // See if it could be a hexadecimal representation 318 // We don't use startsWith because there might be a leading sign. 319 if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) { 320 return HexStringParser.parseFloat(s); 321 } 322 323 StringExponentPair info = initialParse(s, length, false); 324 if (info.infinity || info.zero) { 325 return info.specialValue(); 326 } 327 float result = parseFltImpl(info.s, (int) info.e); 328 if (Float.floatToRawIntBits(result) == 0xffffffff) { 329 throw invalidReal(s, false); 330 } 331 return info.negative ? -result : result; 332 } 333 } 334