001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.luni.util;
019:
020: /**
021: * Used to parse a string and return either a single or double precision
022: * floating point number.
023: */
024: public final class FloatingPointParser {
025:
026: private static final class StringExponentPair {
027: String s;
028:
029: int e;
030:
031: boolean negative;
032:
033: StringExponentPair(String s, int e, boolean negative) {
034: this .s = s;
035: this .e = e;
036: this .negative = negative;
037: }
038: }
039:
040: /**
041: * Takes a String and an integer exponent. The String should hold a positive
042: * integer value (or zero). The exponent will be used to calculate the
043: * floating point number by taking the positive integer the String
044: * represents and multiplying by 10 raised to the power of the of the
045: * exponent. Returns the closest double value to the real number
046: *
047: * @param s
048: * the String that will be parsed to a floating point
049: * @param e
050: * an int represent the 10 to part
051: * @return the double closest to the real number
052: *
053: * @exception NumberFormatException
054: * if the String doesn't represent a positive integer value
055: */
056: private static native double parseDblImpl(String s, int e);
057:
058: /**
059: * Takes a String and an integer exponent. The String should hold a positive
060: * integer value (or zero). The exponent will be used to calculate the
061: * floating point number by taking the positive integer the String
062: * represents and multiplying by 10 raised to the power of the of the
063: * exponent. Returns the closest float value to the real number
064: *
065: * @param s
066: * the String that will be parsed to a floating point
067: * @param e
068: * an int represent the 10 to part
069: * @return the float closest to the real number
070: *
071: * @exception NumberFormatException
072: * if the String doesn't represent a positive integer value
073: */
074: private static native float parseFltImpl(String s, int e);
075:
076: /**
077: * Takes a String and does some initial parsing. Should return a
078: * StringExponentPair containing a String with no leading or trailing white
079: * space and trailing zeroes eliminated. The exponent of the
080: * StringExponentPair will be used to calculate the floating point number by
081: * taking the positive integer the String represents and multiplying by 10
082: * raised to the power of the of the exponent.
083: *
084: * @param s
085: * the String that will be parsed to a floating point
086: * @param length
087: * the length of s
088: * @return a StringExponentPair with necessary values
089: *
090: * @exception NumberFormatException
091: * if the String doesn't pass basic tests
092: */
093: private static StringExponentPair initialParse(String s, int length) {
094: boolean negative = false;
095: char c;
096: int start, end, decimal;
097: int e = 0;
098:
099: start = 0;
100: if (length == 0)
101: throw new NumberFormatException(s);
102:
103: c = s.charAt(length - 1);
104: if (c == 'D' || c == 'd' || c == 'F' || c == 'f') {
105: length--;
106: if (length == 0)
107: throw new NumberFormatException(s);
108: }
109:
110: end = Math.max(s.indexOf('E'), s.indexOf('e'));
111: if (end > -1) {
112: if (end + 1 == length)
113: throw new NumberFormatException(s);
114:
115: int exponent_offset = end + 1;
116: if (s.charAt(exponent_offset) == '+') {
117: if (s.charAt(exponent_offset + 1) == '-') {
118: throw new NumberFormatException(s);
119: }
120: exponent_offset++; // skip the plus sign
121: }
122: try {
123: e = Integer.parseInt(s.substring(exponent_offset,
124: length));
125: } catch (NumberFormatException ex) {
126: // ex contains the exponent substring
127: // only so throw a new exception with
128: // the correct string
129: throw new NumberFormatException(s);
130: }
131:
132: } else {
133: end = length;
134: }
135: if (length == 0)
136: throw new NumberFormatException(s);
137:
138: c = s.charAt(start);
139: if (c == '-') {
140: ++start;
141: --length;
142: negative = true;
143: } else if (c == '+') {
144: ++start;
145: --length;
146: }
147: if (length == 0)
148: throw new NumberFormatException(s);
149:
150: decimal = s.indexOf('.');
151: if (decimal > -1) {
152: e -= end - decimal - 1;
153: s = s.substring(start, decimal)
154: + s.substring(decimal + 1, end);
155: } else {
156: s = s.substring(start, end);
157: }
158:
159: if ((length = s.length()) == 0)
160: throw new NumberFormatException();
161:
162: end = length;
163: while (end > 1 && s.charAt(end - 1) == '0')
164: --end;
165:
166: start = 0;
167: while (start < end - 1 && s.charAt(start) == '0')
168: start++;
169:
170: if (end != length || start != 0) {
171: e += length - end;
172: s = s.substring(start, end);
173: }
174:
175: return new StringExponentPair(s, e, negative);
176: }
177:
178: /*
179: * Assumes the string is trimmed.
180: */
181: private static double parseDblName(String namedDouble, int length) {
182: // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity,
183: // -Infinity.
184: if ((length != 3) && (length != 4) && (length != 8)
185: && (length != 9)) {
186: throw new NumberFormatException();
187: }
188:
189: boolean negative = false;
190: int cmpstart = 0;
191: switch (namedDouble.charAt(0)) {
192: case '-':
193: negative = true; // fall through
194: case '+':
195: cmpstart = 1;
196: default:
197: }
198:
199: if (namedDouble
200: .regionMatches(false, cmpstart, "Infinity", 0, 8)) {
201: return negative ? Double.NEGATIVE_INFINITY
202: : Float.POSITIVE_INFINITY;
203: }
204:
205: if (namedDouble.regionMatches(false, cmpstart, "NaN", 0, 3)) {
206: return Double.NaN;
207: }
208:
209: throw new NumberFormatException();
210: }
211:
212: /*
213: * Assumes the string is trimmed.
214: */
215: private static float parseFltName(String namedFloat, int length) {
216: // Valid strings are only +Nan, NaN, -Nan, +Infinity, Infinity,
217: // -Infinity.
218: if ((length != 3) && (length != 4) && (length != 8)
219: && (length != 9)) {
220: throw new NumberFormatException();
221: }
222:
223: boolean negative = false;
224: int cmpstart = 0;
225: switch (namedFloat.charAt(0)) {
226: case '-':
227: negative = true; // fall through
228: case '+':
229: cmpstart = 1;
230: default:
231: }
232:
233: if (namedFloat.regionMatches(false, cmpstart, "Infinity", 0, 8)) {
234: return negative ? Float.NEGATIVE_INFINITY
235: : Float.POSITIVE_INFINITY;
236: }
237:
238: if (namedFloat.regionMatches(false, cmpstart, "NaN", 0, 3)) {
239: return Float.NaN;
240: }
241:
242: throw new NumberFormatException();
243: }
244:
245: /**
246: * Returns the closest double value to the real number in the string.
247: *
248: * @param s
249: * the String that will be parsed to a floating point
250: * @return the double closest to the real number
251: *
252: * @exception NumberFormatException
253: * if the String doesn't represent a double
254: */
255: public static double parseDouble(String s) {
256: s = s.trim();
257: int length = s.length();
258:
259: if (length == 0) {
260: throw new NumberFormatException(s);
261: }
262:
263: // See if this could be a named double
264: char last = s.charAt(length - 1);
265: if ((last == 'y') || (last == 'N')) {
266: return parseDblName(s, length);
267: }
268:
269: // See if it could be a hexadecimal representation
270: if (s.toLowerCase().indexOf("0x") != -1) { //$NON-NLS-1$
271: return HexStringParser.parseDouble(s);
272: }
273:
274: StringExponentPair info = initialParse(s, length);
275:
276: double result = parseDblImpl(info.s, info.e);
277: if (info.negative)
278: result = -result;
279:
280: return result;
281: }
282:
283: /**
284: * Returns the closest float value to the real number in the string.
285: *
286: * @param s
287: * the String that will be parsed to a floating point
288: * @return the float closest to the real number
289: *
290: * @exception NumberFormatException
291: * if the String doesn't represent a float
292: */
293: public static float parseFloat(String s) {
294: s = s.trim();
295: int length = s.length();
296:
297: if (length == 0) {
298: throw new NumberFormatException(s);
299: }
300:
301: // See if this could be a named float
302: char last = s.charAt(length - 1);
303: if ((last == 'y') || (last == 'N')) {
304: return parseFltName(s, length);
305: }
306:
307: // See if it could be a hexadecimal representation
308: if (s.toLowerCase().indexOf("0x") != -1) { //$NON-NLS-1$
309: return HexStringParser.parseFloat(s);
310: }
311:
312: StringExponentPair info = initialParse(s, length);
313:
314: float result = parseFltImpl(info.s, info.e);
315: if (info.negative)
316: result = -result;
317:
318: return result;
319: }
320: }
|