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