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: import java.util.regex.Matcher;
021: import java.util.regex.Pattern;
022:
023: /*
024: * Parses hex string to a single or double precision floating point number.
025: */
026: final class HexStringParser {
027:
028: private static final int DOUBLE_EXPONENT_WIDTH = 11;
029:
030: private static final int DOUBLE_MANTISSA_WIDTH = 52;
031:
032: private static final int FLOAT_EXPONENT_WIDTH = 8;
033:
034: private static final int FLOAT_MANTISSA_WIDTH = 23;
035:
036: private static final int HEX_RADIX = 16;
037:
038: private static final int MAX_SIGNIFICANT_LENGTH = 15;
039:
040: private static final String HEX_SIGNIFICANT = "0[xX](\\p{XDigit}+\\.?|\\p{XDigit}*\\.\\p{XDigit}+)"; //$NON-NLS-1$
041:
042: private static final String BINARY_EXPONENT = "[pP]([+-]?\\d+)"; //$NON-NLS-1$
043:
044: private static final String FLOAT_TYPE_SUFFIX = "[fFdD]?"; //$NON-NLS-1$
045:
046: private static final String HEX_PATTERN = "[\\x00-\\x20]*([+-]?)" + HEX_SIGNIFICANT //$NON-NLS-1$
047: + BINARY_EXPONENT + FLOAT_TYPE_SUFFIX + "[\\x00-\\x20]*"; //$NON-NLS-1$
048:
049: private static final Pattern PATTERN = Pattern.compile(HEX_PATTERN);
050:
051: private final int EXPONENT_WIDTH;
052:
053: private final int MANTISSA_WIDTH;
054:
055: private final long EXPONENT_BASE;
056:
057: private final long MAX_EXPONENT;
058:
059: private final long MIN_EXPONENT;
060:
061: private final long MANTISSA_MASK;
062:
063: private long sign;
064:
065: private long exponent;
066:
067: private long mantissa;
068:
069: private String abandonedNumber = ""; //$NON-NLS-1$
070:
071: public HexStringParser(int exponent_width, int mantissa_width) {
072: this .EXPONENT_WIDTH = exponent_width;
073: this .MANTISSA_WIDTH = mantissa_width;
074:
075: this .EXPONENT_BASE = ~(-1L << (exponent_width - 1));
076: this .MAX_EXPONENT = ~(-1L << exponent_width);
077: this .MIN_EXPONENT = -(MANTISSA_WIDTH + 1);
078: this .MANTISSA_MASK = ~(-1L << mantissa_width);
079: }
080:
081: /*
082: * Parses the hex string to a double number.
083: */
084: public static double parseDouble(String hexString) {
085: HexStringParser parser = new HexStringParser(
086: DOUBLE_EXPONENT_WIDTH, DOUBLE_MANTISSA_WIDTH);
087: long result = parser.parse(hexString);
088: return Double.longBitsToDouble(result);
089: }
090:
091: /*
092: * Parses the hex string to a float number.
093: */
094: public static float parseFloat(String hexString) {
095: HexStringParser parser = new HexStringParser(
096: FLOAT_EXPONENT_WIDTH, FLOAT_MANTISSA_WIDTH);
097: int result = (int) parser.parse(hexString);
098: return Float.intBitsToFloat(result);
099: }
100:
101: private long parse(String hexString) {
102: String[] hexSegments = getSegmentsFromHexString(hexString);
103: String signStr = hexSegments[0];
104: String significantStr = hexSegments[1];
105: String exponentStr = hexSegments[2];
106:
107: parseHexSign(signStr);
108: parseExponent(exponentStr);
109: parseMantissa(significantStr);
110:
111: sign <<= (MANTISSA_WIDTH + EXPONENT_WIDTH);
112: exponent <<= MANTISSA_WIDTH;
113: return sign | exponent | mantissa;
114: }
115:
116: /*
117: * Analyzes the hex string and extracts the sign and digit segments.
118: */
119: private static String[] getSegmentsFromHexString(String hexString) {
120: Matcher matcher = PATTERN.matcher(hexString);
121: if (!matcher.matches()) {
122: throw new NumberFormatException();
123: }
124:
125: String[] hexSegments = new String[3];
126: hexSegments[0] = matcher.group(1);
127: hexSegments[1] = matcher.group(2);
128: hexSegments[2] = matcher.group(3);
129:
130: return hexSegments;
131: }
132:
133: /*
134: * Parses the sign field.
135: */
136: private void parseHexSign(String signStr) {
137: this .sign = signStr.equals("-") ? 1 : 0; //$NON-NLS-1$
138: }
139:
140: /*
141: * Parses the exponent field.
142: */
143: private void parseExponent(String exponentStr) {
144: char leadingChar = exponentStr.charAt(0);
145: int expSign = (leadingChar == '-' ? -1 : 1);
146: if (!Character.isDigit(leadingChar)) {
147: exponentStr = exponentStr.substring(1);
148: }
149:
150: try {
151: exponent = expSign * Long.parseLong(exponentStr);
152: checkedAddExponent(EXPONENT_BASE);
153: } catch (NumberFormatException e) {
154: exponent = expSign * Long.MAX_VALUE;
155: }
156: }
157:
158: /*
159: * Parses the mantissa field.
160: */
161: private void parseMantissa(String significantStr) {
162: String[] strings = significantStr.split("\\."); //$NON-NLS-1$
163: String strIntegerPart = strings[0];
164: String strDecimalPart = strings.length > 1 ? strings[1] : ""; //$NON-NLS-1$
165:
166: String significand = getNormalizedSignificand(strIntegerPart,
167: strDecimalPart);
168: if (significand.equals("0")) { //$NON-NLS-1$
169: setZero();
170: return;
171: }
172:
173: int offset = getOffset(strIntegerPart, strDecimalPart);
174: checkedAddExponent(offset);
175:
176: if (exponent >= MAX_EXPONENT) {
177: setInfinite();
178: return;
179: }
180:
181: if (exponent <= MIN_EXPONENT) {
182: setZero();
183: return;
184: }
185:
186: if (significand.length() > MAX_SIGNIFICANT_LENGTH) {
187: abandonedNumber = significand
188: .substring(MAX_SIGNIFICANT_LENGTH);
189: significand = significand.substring(0,
190: MAX_SIGNIFICANT_LENGTH);
191: }
192:
193: mantissa = Long.parseLong(significand, HEX_RADIX);
194:
195: if (exponent >= 1) {
196: processNormalNumber();
197: } else {
198: processSubNormalNumber();
199: }
200:
201: }
202:
203: private void setInfinite() {
204: exponent = MAX_EXPONENT;
205: mantissa = 0;
206: }
207:
208: private void setZero() {
209: exponent = 0;
210: mantissa = 0;
211: }
212:
213: /*
214: * Sets the exponent variable to Long.MAX_VALUE or -Long.MAX_VALUE if
215: * overflow or underflow happens.
216: */
217: private void checkedAddExponent(long offset) {
218: long result = exponent + offset;
219: int expSign = Long.signum(exponent);
220: if (expSign * Long.signum(offset) > 0
221: && expSign * Long.signum(result) < 0) {
222: exponent = expSign * Long.MAX_VALUE;
223: } else {
224: exponent = result;
225: }
226: }
227:
228: private void processNormalNumber() {
229: int desiredWidth = MANTISSA_WIDTH + 2;
230: fitMantissaInDesiredWidth(desiredWidth);
231: round();
232: mantissa = mantissa & MANTISSA_MASK;
233: }
234:
235: private void processSubNormalNumber() {
236: int desiredWidth = MANTISSA_WIDTH + 1;
237: desiredWidth += (int) exponent;//lends bit from mantissa to exponent
238: exponent = 0;
239: fitMantissaInDesiredWidth(desiredWidth);
240: round();
241: mantissa = mantissa & MANTISSA_MASK;
242: }
243:
244: /*
245: * Adjusts the mantissa to desired width for further analysis.
246: */
247: private void fitMantissaInDesiredWidth(int desiredWidth) {
248: int bitLength = countBitsLength(mantissa);
249: if (bitLength > desiredWidth) {
250: discardTrailingBits(bitLength - desiredWidth);
251: } else {
252: mantissa <<= (desiredWidth - bitLength);
253: }
254: }
255:
256: /*
257: * Stores the discarded bits to abandonedNumber.
258: */
259: private void discardTrailingBits(long num) {
260: long mask = ~(-1L << num);
261: abandonedNumber += (mantissa & mask);
262: mantissa >>= num;
263: }
264:
265: /*
266: * The value is rounded up or down to the nearest infinitely precise result.
267: * If the value is exactly halfway between two infinitely precise results,
268: * then it should be rounded up to the nearest infinitely precise even.
269: */
270: private void round() {
271: String result = abandonedNumber.replaceAll("0+", ""); //$NON-NLS-1$ //$NON-NLS-2$
272: boolean moreThanZero = (result.length() > 0 ? true : false);
273:
274: int lastDiscardedBit = (int) (mantissa & 1L);
275: mantissa >>= 1;
276: int tailBitInMantissa = (int) (mantissa & 1L);
277:
278: if (lastDiscardedBit == 1
279: && (moreThanZero || tailBitInMantissa == 1)) {
280: int oldLength = countBitsLength(mantissa);
281: mantissa += 1L;
282: int newLength = countBitsLength(mantissa);
283:
284: //Rounds up to exponent when whole bits of mantissa are one-bits.
285: if (oldLength >= MANTISSA_WIDTH && newLength > oldLength) {
286: checkedAddExponent(1);
287: }
288: }
289: }
290:
291: /*
292: * Returns the normalized significand after removing the leading zeros.
293: */
294: private String getNormalizedSignificand(String strIntegerPart,
295: String strDecimalPart) {
296: String significand = strIntegerPart + strDecimalPart;
297: significand = significand.replaceFirst("^0+", ""); //$NON-NLS-1$//$NON-NLS-2$
298: if (significand.length() == 0) {
299: significand = "0"; //$NON-NLS-1$
300: }
301: return significand;
302: }
303:
304: /*
305: * Calculates the offset between the normalized number and unnormalized
306: * number. In a normalized representation, significand is represented by the
307: * characters "0x1." followed by a lowercase hexadecimal representation of
308: * the rest of the significand as a fraction.
309: */
310: private int getOffset(String strIntegerPart, String strDecimalPart) {
311: strIntegerPart = strIntegerPart.replaceFirst("^0+", ""); //$NON-NLS-1$ //$NON-NLS-2$
312:
313: //If the Integer part is a nonzero number.
314: if (strIntegerPart.length() != 0) {
315: String leadingNumber = strIntegerPart.substring(0, 1);
316: return (strIntegerPart.length() - 1)
317: * 4
318: + countBitsLength(Long.parseLong(leadingNumber,
319: HEX_RADIX)) - 1;
320: }
321:
322: //If the Integer part is a zero number.
323: int i;
324: for (i = 0; i < strDecimalPart.length()
325: && strDecimalPart.charAt(i) == '0'; i++)
326: ;
327: if (i == strDecimalPart.length()) {
328: return 0;
329: }
330: String leadingNumber = strDecimalPart.substring(i, i + 1);
331: return (-i - 1)
332: * 4
333: + countBitsLength(Long.parseLong(leadingNumber,
334: HEX_RADIX)) - 1;
335: }
336:
337: private int countBitsLength(long value) {
338: int leadingZeros = Long.numberOfLeadingZeros(value);
339: return Long.SIZE - leadingZeros;
340: }
341: }
|