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
18package java.lang;
19
20import java.util.regex.Matcher;
21import java.util.regex.Pattern;
22
23/*
24 * Parses hex string to a single or double precision floating point number.
25 *
26 * TODO: rewrite this!
27 *
28 * @hide
29 */
30final class HexStringParser {
31
32    private static final int DOUBLE_EXPONENT_WIDTH = 11;
33
34    private static final int DOUBLE_MANTISSA_WIDTH = 52;
35
36    private static final int FLOAT_EXPONENT_WIDTH = 8;
37
38    private static final int FLOAT_MANTISSA_WIDTH = 23;
39
40    private static final int HEX_RADIX = 16;
41
42    private static final int MAX_SIGNIFICANT_LENGTH = 15;
43
44    private static final String HEX_SIGNIFICANT = "0[xX](\\p{XDigit}+\\.?|\\p{XDigit}*\\.\\p{XDigit}+)";
45
46    private static final String BINARY_EXPONENT = "[pP]([+-]?\\d+)";
47
48    private static final String FLOAT_TYPE_SUFFIX = "[fFdD]?";
49
50    private static final String HEX_PATTERN = "[\\x00-\\x20]*([+-]?)" + HEX_SIGNIFICANT
51            + BINARY_EXPONENT + FLOAT_TYPE_SUFFIX + "[\\x00-\\x20]*";
52
53    private static final Pattern PATTERN = Pattern.compile(HEX_PATTERN);
54
55    private final int EXPONENT_WIDTH;
56
57    private final int MANTISSA_WIDTH;
58
59    private final long EXPONENT_BASE;
60
61    private final long MAX_EXPONENT;
62
63    private final long MIN_EXPONENT;
64
65    private final long MANTISSA_MASK;
66
67    private long sign;
68
69    private long exponent;
70
71    private long mantissa;
72
73    private String abandonedNumber="";
74
75    public HexStringParser(int exponentWidth, int mantissaWidth) {
76        this.EXPONENT_WIDTH = exponentWidth;
77        this.MANTISSA_WIDTH = mantissaWidth;
78
79        this.EXPONENT_BASE = ~(-1L << (exponentWidth - 1));
80        this.MAX_EXPONENT = ~(-1L << exponentWidth);
81        this.MIN_EXPONENT = -(MANTISSA_WIDTH + 1);
82        this.MANTISSA_MASK = ~(-1L << mantissaWidth);
83    }
84
85    /*
86     * Parses the hex string to a double number.
87     */
88    public static double parseDouble(String hexString) {
89        HexStringParser parser = new HexStringParser(DOUBLE_EXPONENT_WIDTH, DOUBLE_MANTISSA_WIDTH);
90        long result = parser.parse(hexString, true);
91        return Double.longBitsToDouble(result);
92    }
93
94    /*
95     * Parses the hex string to a float number.
96     */
97    public static float parseFloat(String hexString) {
98        HexStringParser parser = new HexStringParser(FLOAT_EXPONENT_WIDTH, FLOAT_MANTISSA_WIDTH);
99        int result = (int) parser.parse(hexString, false);
100        return Float.intBitsToFloat(result);
101    }
102
103    private long parse(String hexString, boolean isDouble) {
104        Matcher matcher = PATTERN.matcher(hexString);
105        if (!matcher.matches()) {
106            throw new NumberFormatException("Invalid hex " + (isDouble ? "double" : "float")+ ":" +
107                    hexString);
108        }
109
110        String signStr = matcher.group(1);
111        String significantStr = matcher.group(2);
112        String exponentStr = matcher.group(3);
113
114        parseHexSign(signStr);
115        parseExponent(exponentStr);
116        parseMantissa(significantStr);
117
118        sign <<= (MANTISSA_WIDTH + EXPONENT_WIDTH);
119        exponent <<= MANTISSA_WIDTH;
120        return sign | exponent | mantissa;
121    }
122
123    /*
124     * Parses the sign field.
125     */
126    private void parseHexSign(String signStr) {
127        this.sign = signStr.equals("-") ? 1 : 0;
128    }
129
130    /*
131     * Parses the exponent field.
132     */
133    private void parseExponent(String exponentStr) {
134        char leadingChar = exponentStr.charAt(0);
135        int expSign = (leadingChar == '-' ? -1 : 1);
136        if (!Character.isDigit(leadingChar)) {
137            exponentStr = exponentStr.substring(1);
138        }
139
140        try {
141            exponent = expSign * Long.parseLong(exponentStr);
142            checkedAddExponent(EXPONENT_BASE);
143        } catch (NumberFormatException e) {
144            exponent = expSign * Long.MAX_VALUE;
145        }
146    }
147
148    /*
149     * Parses the mantissa field.
150     */
151    private void parseMantissa(String significantStr) {
152        String[] strings = significantStr.split("\\.");
153        String strIntegerPart = strings[0];
154        String strDecimalPart = strings.length > 1 ? strings[1] : "";
155
156        String significand = getNormalizedSignificand(strIntegerPart,strDecimalPart);
157        if (significand.equals("0")) {
158            setZero();
159            return;
160        }
161
162        int offset = getOffset(strIntegerPart, strDecimalPart);
163        checkedAddExponent(offset);
164
165        if (exponent >= MAX_EXPONENT) {
166            setInfinite();
167            return;
168        }
169
170        if (exponent <= MIN_EXPONENT) {
171            setZero();
172            return;
173        }
174
175        if (significand.length() > MAX_SIGNIFICANT_LENGTH) {
176            abandonedNumber = significand.substring(MAX_SIGNIFICANT_LENGTH);
177            significand = significand.substring(0, MAX_SIGNIFICANT_LENGTH);
178        }
179
180        mantissa = Long.parseLong(significand, HEX_RADIX);
181
182        if (exponent >= 1) {
183            processNormalNumber();
184        } else{
185            processSubNormalNumber();
186        }
187
188    }
189
190    private void setInfinite() {
191        exponent = MAX_EXPONENT;
192        mantissa = 0;
193    }
194
195    private void setZero() {
196        exponent = 0;
197        mantissa = 0;
198    }
199
200    /*
201     * Sets the exponent variable to Long.MAX_VALUE or -Long.MAX_VALUE if
202     * overflow or underflow happens.
203     */
204    private void checkedAddExponent(long offset) {
205        long result = exponent + offset;
206        int expSign = Long.signum(exponent);
207        if (expSign * Long.signum(offset) > 0 && expSign * Long.signum(result) < 0) {
208            exponent = expSign * Long.MAX_VALUE;
209        } else {
210            exponent = result;
211        }
212    }
213
214    private void processNormalNumber(){
215        int desiredWidth = MANTISSA_WIDTH + 2;
216        fitMantissaInDesiredWidth(desiredWidth);
217        round();
218        mantissa = mantissa & MANTISSA_MASK;
219    }
220
221    private void processSubNormalNumber(){
222        int desiredWidth = MANTISSA_WIDTH + 1;
223        desiredWidth += (int)exponent;//lends bit from mantissa to exponent
224        exponent = 0;
225        fitMantissaInDesiredWidth(desiredWidth);
226        round();
227        mantissa = mantissa & MANTISSA_MASK;
228    }
229
230    /*
231     * Adjusts the mantissa to desired width for further analysis.
232     */
233    private void fitMantissaInDesiredWidth(int desiredWidth){
234        int bitLength = countBitsLength(mantissa);
235        if (bitLength > desiredWidth) {
236            discardTrailingBits(bitLength - desiredWidth);
237        } else {
238            mantissa <<= (desiredWidth - bitLength);
239        }
240    }
241
242    /*
243     * Stores the discarded bits to abandonedNumber.
244     */
245    private void discardTrailingBits(long num) {
246        long mask = ~(-1L << num);
247        abandonedNumber += (mantissa & mask);
248        mantissa >>= num;
249    }
250
251    /*
252     * The value is rounded up or down to the nearest infinitely precise result.
253     * If the value is exactly halfway between two infinitely precise results,
254     * then it should be rounded up to the nearest infinitely precise even.
255     */
256    private void round() {
257        String result = abandonedNumber.replaceAll("0+", "");
258        boolean moreThanZero = (result.length() > 0 ? true : false);
259
260        int lastDiscardedBit = (int) (mantissa & 1L);
261        mantissa >>= 1;
262        int tailBitInMantissa = (int) (mantissa & 1L);
263
264        if (lastDiscardedBit == 1 && (moreThanZero || tailBitInMantissa == 1)) {
265            int oldLength = countBitsLength(mantissa);
266            mantissa += 1L;
267            int newLength = countBitsLength(mantissa);
268
269            //Rounds up to exponent when whole bits of mantissa are one-bits.
270            if (oldLength >= MANTISSA_WIDTH && newLength > oldLength) {
271                checkedAddExponent(1);
272            }
273        }
274    }
275
276    /*
277     * Returns the normalized significand after removing the leading zeros.
278     */
279    private String getNormalizedSignificand(String strIntegerPart, String strDecimalPart) {
280        String significand = strIntegerPart + strDecimalPart;
281        significand = significand.replaceFirst("^0+", "");
282        if (significand.length() == 0) {
283            significand = "0";
284        }
285        return significand;
286    }
287
288    /*
289     * Calculates the offset between the normalized number and unnormalized
290     * number. In a normalized representation, significand is represented by the
291     * characters "0x1." followed by a lowercase hexadecimal representation of
292     * the rest of the significand as a fraction.
293     */
294    private int getOffset(String strIntegerPart, String strDecimalPart) {
295        strIntegerPart = strIntegerPart.replaceFirst("^0+", "");
296
297        //If the Integer part is a nonzero number.
298        if (strIntegerPart.length() != 0) {
299            String leadingNumber = strIntegerPart.substring(0, 1);
300            return (strIntegerPart.length() - 1) * 4 + countBitsLength(Long.parseLong(leadingNumber,HEX_RADIX)) - 1;
301        }
302
303        //If the Integer part is a zero number.
304        int i;
305        for (i = 0; i < strDecimalPart.length() && strDecimalPart.charAt(i) == '0'; i++);
306        if (i == strDecimalPart.length()) {
307            return 0;
308        }
309        String leadingNumber=strDecimalPart.substring(i,i + 1);
310        return (-i - 1) * 4 + countBitsLength(Long.parseLong(leadingNumber, HEX_RADIX)) - 1;
311    }
312
313    private int countBitsLength(long value) {
314        int leadingZeros = Long.numberOfLeadingZeros(value);
315        return Long.SIZE - leadingZeros;
316    }
317}
318