Home | History | Annotate | Download | only in lang
      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 
     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