Home | History | Annotate | Download | only in lang
      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 int.
    126                 if (negativeExponent) {
    127                     result.zero = true;
    128                 } else {
    129                     result.infinity = true;
    130                 }
    131                 return result;
    132             }
    133         } else {
    134             end = length;
    135         }
    136         if (length == 0) {
    137             throw invalidReal(s, isDouble);
    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         int decimal = s.indexOf('.');
    155         if (decimal > -1) {
    156             result.e -= end - decimal - 1;
    157             s = s.substring(start, decimal) + s.substring(decimal + 1, end);
    158         } else {
    159             s = s.substring(start, end);
    160         }
    161 
    162         if ((length = s.length()) == 0) {
    163             throw invalidReal(s, isDouble);
    164         }
    165 
    166         end = length;
    167         while (end > 1 && s.charAt(end - 1) == '0') {
    168             --end;
    169         }
    170 
    171         start = 0;
    172         while (start < end - 1 && s.charAt(start) == '0') {
    173             start++;
    174         }
    175 
    176         if (end != length || start != 0) {
    177             result.e += length - end;
    178             s = s.substring(start, end);
    179         }
    180 
    181         // This is a hack for https://issues.apache.org/jira/browse/HARMONY-329
    182         // Trim the length of very small numbers, natives can only handle down
    183         // to E-309
    184         final int APPROX_MIN_MAGNITUDE = -359;
    185         final int MAX_DIGITS = 52;
    186         length = s.length();
    187         if (length > MAX_DIGITS && result.e < APPROX_MIN_MAGNITUDE) {
    188             int d = Math.min(APPROX_MIN_MAGNITUDE - (int) result.e, length - 1);
    189             s = s.substring(0, length - d);
    190             result.e += d;
    191         }
    192 
    193         // This is a hack for https://issues.apache.org/jira/browse/HARMONY-6641
    194         // The magic 1024 was determined experimentally; the more plausible -324 and +309 were
    195         // not sufficient to pass both our tests and harmony's tests.
    196         if (result.e < -1024) {
    197             result.zero = true;
    198             return result;
    199         } else if (result.e > 1024) {
    200             result.infinity = true;
    201             return result;
    202         }
    203 
    204         result.s = s;
    205         return result;
    206     }
    207 
    208     // Parses "+Nan", "NaN", "-Nan", "+Infinity", "Infinity", and "-Infinity", case-insensitively.
    209     private static float parseName(String name, boolean isDouble) {
    210         // Explicit sign?
    211         boolean negative = false;
    212         int i = 0;
    213         int length = name.length();
    214         char firstChar = name.charAt(i);
    215         if (firstChar == '-') {
    216             negative = true;
    217             ++i;
    218             --length;
    219         } else if (firstChar == '+') {
    220             ++i;
    221             --length;
    222         }
    223 
    224         if (length == 8 && name.regionMatches(false, i, "Infinity", 0, 8)) {
    225             return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
    226         }
    227         if (length == 3 && name.regionMatches(false, i, "NaN", 0, 3)) {
    228             return Float.NaN;
    229         }
    230         throw invalidReal(name, isDouble);
    231     }
    232 
    233     /**
    234      * Returns the closest double value to the real number in the string.
    235      *
    236      * @param s
    237      *            the String that will be parsed to a floating point
    238      * @return the double closest to the real number
    239      *
    240      * @exception NumberFormatException
    241      *                if the String doesn't represent a double
    242      */
    243     public static double parseDouble(String s) {
    244         s = s.trim();
    245         int length = s.length();
    246 
    247         if (length == 0) {
    248             throw invalidReal(s, true);
    249         }
    250 
    251         // See if this could be a named double
    252         char last = s.charAt(length - 1);
    253         if (last == 'y' || last == 'N') {
    254             return parseName(s, true);
    255         }
    256 
    257         // See if it could be a hexadecimal representation.
    258         // We don't use startsWith because there might be a leading sign.
    259         if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) {
    260             return HexStringParser.parseDouble(s);
    261         }
    262 
    263         StringExponentPair info = initialParse(s, length, true);
    264         if (info.infinity || info.zero) {
    265             return info.specialValue();
    266         }
    267         double result = parseDblImpl(info.s, (int) info.e);
    268         if (Double.doubleToRawLongBits(result) == 0xffffffffffffffffL) {
    269             throw invalidReal(s, true);
    270         }
    271         return info.negative ? -result : result;
    272     }
    273 
    274     /**
    275      * Returns the closest float value to the real number in the string.
    276      *
    277      * @param s
    278      *            the String that will be parsed to a floating point
    279      * @return the float closest to the real number
    280      *
    281      * @exception NumberFormatException
    282      *                if the String doesn't represent a float
    283      */
    284     public static float parseFloat(String s) {
    285         s = s.trim();
    286         int length = s.length();
    287 
    288         if (length == 0) {
    289             throw invalidReal(s, false);
    290         }
    291 
    292         // See if this could be a named float
    293         char last = s.charAt(length - 1);
    294         if (last == 'y' || last == 'N') {
    295             return parseName(s, false);
    296         }
    297 
    298         // See if it could be a hexadecimal representation
    299         // We don't use startsWith because there might be a leading sign.
    300         if (s.indexOf("0x") != -1 || s.indexOf("0X") != -1) {
    301             return HexStringParser.parseFloat(s);
    302         }
    303 
    304         StringExponentPair info = initialParse(s, length, false);
    305         if (info.infinity || info.zero) {
    306             return info.specialValue();
    307         }
    308         float result = parseFltImpl(info.s, (int) info.e);
    309         if (Float.floatToRawIntBits(result) == 0xffffffff) {
    310             throw invalidReal(s, false);
    311         }
    312         return info.negative ? -result : result;
    313     }
    314 }
    315