| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.luni.util; |
| |
| |
| /** |
| * Used to parse a string and return either a single or double precision |
| * floating point number. |
| */ |
| public final class FloatingPointParser { |
| |
| private static final class StringExponentPair { |
| String s; |
| |
| int e; |
| |
| boolean negative; |
| |
| StringExponentPair(String s, int e, boolean negative) { |
| this.s = s; |
| this.e = e; |
| this.negative = negative; |
| } |
| } |
| |
| /** |
| * Takes a String and an integer exponent. The String should hold a positive |
| * integer value (or zero). The exponent will be used to calculate the |
| * floating point number by taking the positive integer the String |
| * represents and multiplying by 10 raised to the power of the of the |
| * exponent. Returns the closest double value to the real number |
| * |
| * @param s |
| * the String that will be parsed to a floating point |
| * @param e |
| * an int represent the 10 to part |
| * @return the double closest to the real number |
| * |
| * @exception NumberFormatException |
| * if the String doesn't represent a positive integer value |
| */ |
| private static native double parseDblImpl(String s, int e); |
| |
| /** |
| * Takes a String and an integer exponent. The String should hold a positive |
| * integer value (or zero). The exponent will be used to calculate the |
| * floating point number by taking the positive integer the String |
| * represents and multiplying by 10 raised to the power of the of the |
| * exponent. Returns the closest float value to the real number |
| * |
| * @param s |
| * the String that will be parsed to a floating point |
| * @param e |
| * an int represent the 10 to part |
| * @return the float closest to the real number |
| * |
| * @exception NumberFormatException |
| * if the String doesn't represent a positive integer value |
| */ |
| private static native float parseFltImpl(String s, int e); |
| |
| /** |
| * Takes a String and does some initial parsing. Should return a |
| * StringExponentPair containing a String with no leading or trailing white |
| * space and trailing zeroes eliminated. The exponent of the |
| * StringExponentPair will be used to calculate the floating point number by |
| * taking the positive integer the String represents and multiplying by 10 |
| * raised to the power of the of the exponent. |
| * |
| * @param s |
| * the String that will be parsed to a floating point |
| * @param length |
| * the length of s |
| * @return a StringExponentPair with necessary values |
| * |
| * @exception NumberFormatException |
| * if the String doesn't pass basic tests |
| */ |
| private static StringExponentPair initialParse(String s, int length) { |
| boolean negative = false; |
| char c; |
| int start, end, decimal; |
| int e = 0; |
| |
| start = 0; |
| if (length == 0) |
| throw new NumberFormatException(s); |
| |
| c = s.charAt(length - 1); |
| if (c == 'D' || c == 'd' || c == 'F' || c == 'f') { |
| length--; |
| if (length == 0) |
| throw new NumberFormatException(s); |
| } |
| |
| end = Math.max(s.indexOf('E'), s.indexOf('e')); |
| if (end > -1) { |
| if (end + 1 == length) |
| throw new NumberFormatException(s); |
| |
| int exponent_offset = end + 1; |
| if (s.charAt(exponent_offset) == '+') { |
| if (s.charAt(exponent_offset + 1) == '-') { |
| throw new NumberFormatException(s); |
| } |
| exponent_offset++; // skip the plus sign |
| } |
| try { |
| e = Integer.parseInt(s.substring(exponent_offset, |
| length)); |
| } catch (NumberFormatException ex) { |
| // ex contains the exponent substring |
| // only so throw a new exception with |
| // the correct string |
| throw new NumberFormatException(s); |
| } |
| |
| } else { |
| end = length; |
| } |
| if (length == 0) |
| throw new NumberFormatException(s); |
| |
| c = s.charAt(start); |
| if (c == '-') { |
| ++start; |
| --length; |
| negative = true; |
| } else if (c == '+') { |
| ++start; |
| --length; |
| } |
| if (length == 0) |
| throw new NumberFormatException(s); |
| |
| decimal = s.indexOf('.'); |
| if (decimal > -1) { |
| e -= end - decimal - 1; |
| s = s.substring(start, decimal) + s.substring(decimal + 1, end); |
| } else { |
| s = s.substring(start, end); |
| } |
| |
| if ((length = s.length()) == 0) |
| throw new NumberFormatException(); |
| |
| end = length; |
| while (end > 1 && s.charAt(end - 1) == '0') |
| --end; |
| |
| start = 0; |
| while (start < end - 1 && s.charAt(start) == '0') |
| start++; |
| |
| if (end != length || start != 0) { |
| e += length - end; |
| s = s.substring(start, end); |
| } |
| |
| // Trim the length of very small numbers, natives can only handle down |
| // to E-309 |
| final int APPROX_MIN_MAGNITUDE = -359; |
| final int MAX_DIGITS = 52; |
| length = s.length(); |
| if (length > MAX_DIGITS && e < APPROX_MIN_MAGNITUDE) { |
| int d = Math.min(APPROX_MIN_MAGNITUDE - e, length - 1); |
| s = s.substring(0, length - d); |
| e += d; |
| } |
| |
| return new StringExponentPair(s, e, negative); |
| } |
| |
| /* |
| * Assumes the string is trimmed. |
| */ |
| private static double parseDblName(String namedDouble, int length) { |
| // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, |
| // -Infinity. |
| if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { |
| throw new NumberFormatException(); |
| } |
| |
| boolean negative = false; |
| int cmpstart = 0; |
| switch (namedDouble.charAt(0)) { |
| case '-': |
| negative = true; // fall through |
| case '+': |
| cmpstart = 1; |
| default: |
| } |
| |
| if (namedDouble.regionMatches(false, cmpstart, "Infinity", 0, 8)) { |
| return negative ? Double.NEGATIVE_INFINITY |
| : Float.POSITIVE_INFINITY; |
| } |
| |
| if (namedDouble.regionMatches(false, cmpstart, "NaN", 0, 3)) { |
| return Double.NaN; |
| } |
| |
| throw new NumberFormatException(); |
| } |
| |
| /* |
| * Assumes the string is trimmed. |
| */ |
| private static float parseFltName(String namedFloat, int length) { |
| // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity, |
| // -Infinity. |
| if ((length != 3) && (length != 4) && (length != 8) && (length != 9)) { |
| throw new NumberFormatException(); |
| } |
| |
| boolean negative = false; |
| int cmpstart = 0; |
| switch (namedFloat.charAt(0)) { |
| case '-': |
| negative = true; // fall through |
| case '+': |
| cmpstart = 1; |
| default: |
| } |
| |
| if (namedFloat.regionMatches(false, cmpstart, "Infinity", 0, 8)) { |
| return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; |
| } |
| |
| if (namedFloat.regionMatches(false, cmpstart, "NaN", 0, 3)) { |
| return Float.NaN; |
| } |
| |
| throw new NumberFormatException(); |
| } |
| |
| /** |
| * Returns the closest double value to the real number in the string. |
| * |
| * @param s |
| * the String that will be parsed to a floating point |
| * @return the double closest to the real number |
| * |
| * @exception NumberFormatException |
| * if the String doesn't represent a double |
| */ |
| public static double parseDouble(String s) { |
| s = s.trim(); |
| int length = s.length(); |
| |
| if (length == 0) { |
| throw new NumberFormatException(s); |
| } |
| |
| // See if this could be a named double |
| char last = s.charAt(length - 1); |
| if ((last == 'y') || (last == 'N')) { |
| return parseDblName(s, length); |
| } |
| |
| // See if it could be a hexadecimal representation |
| if (s.toLowerCase().indexOf("0x") != -1) { |
| return HexStringParser.parseDouble(s); |
| } |
| |
| StringExponentPair info = initialParse(s, length); |
| |
| double result = parseDblImpl(info.s, info.e); |
| if (info.negative) |
| result = -result; |
| |
| return result; |
| } |
| |
| /** |
| * Returns the closest float value to the real number in the string. |
| * |
| * @param s |
| * the String that will be parsed to a floating point |
| * @return the float closest to the real number |
| * |
| * @exception NumberFormatException |
| * if the String doesn't represent a float |
| */ |
| public static float parseFloat(String s) { |
| s = s.trim(); |
| int length = s.length(); |
| |
| if (length == 0) { |
| throw new NumberFormatException(s); |
| } |
| |
| // See if this could be a named float |
| char last = s.charAt(length - 1); |
| if ((last == 'y') || (last == 'N')) { |
| return parseFltName(s, length); |
| } |
| |
| // See if it could be a hexadecimal representation |
| if (s.toLowerCase().indexOf("0x") != -1) { |
| return HexStringParser.parseFloat(s); |
| } |
| |
| StringExponentPair info = initialParse(s, length); |
| |
| float result = parseFltImpl(info.s, info.e); |
| if (info.negative) |
| result = -result; |
| |
| return result; |
| } |
| } |