• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 import java.util.regex.Matcher;
21 import 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  */
30 final 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 
HexStringParser(int exponentWidth, int mantissaWidth)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      */
parseDouble(String hexString)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      */
parseFloat(String hexString)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 
parse(String hexString, boolean isDouble)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      */
parseHexSign(String signStr)126     private void parseHexSign(String signStr) {
127         this.sign = signStr.equals("-") ? 1 : 0;
128     }
129 
130     /*
131      * Parses the exponent field.
132      */
parseExponent(String exponentStr)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      */
parseMantissa(String significantStr)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 
setInfinite()190     private void setInfinite() {
191         exponent = MAX_EXPONENT;
192         mantissa = 0;
193     }
194 
setZero()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      */
checkedAddExponent(long offset)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 
processNormalNumber()214     private void processNormalNumber(){
215         int desiredWidth = MANTISSA_WIDTH + 2;
216         fitMantissaInDesiredWidth(desiredWidth);
217         round();
218         mantissa = mantissa & MANTISSA_MASK;
219     }
220 
processSubNormalNumber()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      */
fitMantissaInDesiredWidth(int desiredWidth)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      */
discardTrailingBits(long num)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      */
round()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      */
getNormalizedSignificand(String strIntegerPart, String strDecimalPart)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      */
getOffset(String strIntegerPart, String strDecimalPart)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 
countBitsLength(long value)313     private int countBitsLength(long value) {
314         int leadingZeros = Long.numberOfLeadingZeros(value);
315         return Long.SIZE - leadingZeros;
316     }
317 }
318