/******************************************************************************* * Copyright (c) 2004, 2009 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler.util; /** * Internal utility for declaing with hexadecimal double and float literals. * * @since 3.1 */ public class FloatUtil { private static final int DOUBLE_FRACTION_WIDTH= 52; private static final int DOUBLE_PRECISION= 53; private static final int MAX_DOUBLE_EXPONENT= +1023; private static final int MIN_NORMALIZED_DOUBLE_EXPONENT= -1022; private static final int MIN_UNNORMALIZED_DOUBLE_EXPONENT= MIN_NORMALIZED_DOUBLE_EXPONENT - DOUBLE_PRECISION; private static final int DOUBLE_EXPONENT_BIAS= +1023; private static final int DOUBLE_EXPONENT_SHIFT= 52; private static final int SINGLE_FRACTION_WIDTH= 23; private static final int SINGLE_PRECISION= 24; private static final int MAX_SINGLE_EXPONENT= +127; private static final int MIN_NORMALIZED_SINGLE_EXPONENT= -126; private static final int MIN_UNNORMALIZED_SINGLE_EXPONENT= MIN_NORMALIZED_SINGLE_EXPONENT - SINGLE_PRECISION; private static final int SINGLE_EXPONENT_BIAS= +127; private static final int SINGLE_EXPONENT_SHIFT= 23; /** * Returns the float value corresponding to the given hexadecimal floating-point single * precision literal. The literal must be syntactially correct, and must be a float literal (end * in a 'f' or 'F'). It must not include either leading or trailing whitespace or a sign. * <p> * This method returns the same answer as Float.parseFloat(new String(source)) does in JDK 1.5, * except that this method returns Floal.NaN if it would underflow to 0 (parseFloat just returns * 0). The method handles all the tricky cases, including fraction rounding to 24 bits and * gradual underflow. * </p> * * @param source source string containing single precision hexadecimal floating-point literal * @return the float value, including Float.POSITIVE_INFINITY if the non-zero value is too large * to be represented, and Float.NaN if the non-zero value is too small to be represented */ public static float valueOfHexFloatLiteral(char[] source) { long bits= convertHexFloatingPointLiteralToBits(source); return Float.intBitsToFloat((int)bits); } /** * Returns the double value corresponding to the given hexadecimal floating-point double * precision literal. The literal must be syntactially correct, and must be a double literal * (end in an optional 'd' or 'D'). It must not include either leading or trailing whitespace or * a sign. * <p> * This method returns the same answer as Double.parseDouble(new String(source)) does in JDK * 1.5, except that this method throw NumberFormatException in the case of overflow to infinity * or underflow to 0. The method handles all the tricky cases, including fraction rounding to 53 * bits and gradual underflow. * </p> * * @param source source string containing double precision hexadecimal floating-point literal * @return the double value, including Double.POSITIVE_INFINITY if the non-zero value is too * large to be represented, and Double.NaN if the non-zero value is too small to be * represented */ public static double valueOfHexDoubleLiteral(char[] source) { long bits= convertHexFloatingPointLiteralToBits(source); return Double.longBitsToDouble(bits); } /** * Returns the given hexadecimal floating-point literal as the bits for a single-precision * (float) or a double-precision (double) IEEE floating point number. The literal must be * syntactially correct. It must not include either leading or trailing whitespace or a sign. * * @param source source string containing hexadecimal floating-point literal * @return for double precision literals, bits suitable for passing to Double.longBitsToDouble; * for single precision literals, bits suitable for passing to Single.intBitsToDouble in * the bottom 32 bits of the result * @throws NumberFormatException if the number cannot be parsed */ private static long convertHexFloatingPointLiteralToBits(char[] source) { int length= source.length; long mantissa= 0; // Step 1: process the '0x' lead-in int next= 0; char nextChar= source[next]; nextChar= source[next]; if (nextChar == '0') { next++; } else { throw new NumberFormatException(); } nextChar= source[next]; if (nextChar == 'X' || nextChar == 'x') { next++; } else { throw new NumberFormatException(); } // Step 2: process leading '0's either before or after the '.' int binaryPointPosition= -1; loop: while (true) { nextChar= source[next]; switch (nextChar) { case '0': next++; continue loop; case '.': binaryPointPosition= next; next++; continue loop; default: break loop; } } // Step 3: process the mantissa // leading zeros have been trimmed int mantissaBits= 0; int leadingDigitPosition= -1; loop: while (true) { nextChar= source[next]; int hexdigit; switch (nextChar) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': hexdigit= nextChar - '0'; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': hexdigit= (nextChar - 'a') + 10; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': hexdigit= (nextChar - 'A') + 10; break; case '.': binaryPointPosition= next; next++; continue loop; default: if (binaryPointPosition < 0) { // record virtual '.' as being to right of all digits binaryPointPosition= next; } break loop; } if (mantissaBits == 0) { // this is the first non-zero hex digit // ignore leading binary 0's in hex digit leadingDigitPosition= next; mantissa= hexdigit; mantissaBits= 4; } else if (mantissaBits < 60) { // middle hex digits mantissa<<= 4; mantissa|= hexdigit; mantissaBits+= 4; } else { // more mantissa bits than we can handle // drop this hex digit on the ground } next++; continue loop; } // Step 4: process the 'P' nextChar= source[next]; if (nextChar == 'P' || nextChar == 'p') { next++; } else { throw new NumberFormatException(); } // Step 5: process the exponent int exponent= 0; int exponentSign= +1; loop: while (next < length) { nextChar= source[next]; switch (nextChar) { case '+': exponentSign= +1; next++; continue loop; case '-': exponentSign= -1; next++; continue loop; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': int digit= nextChar - '0'; exponent= (exponent * 10) + digit; next++; continue loop; default: break loop; } } // Step 6: process the optional 'f' or 'd' boolean doublePrecision= true; if (next < length) { nextChar= source[next]; switch (nextChar) { case 'f': case 'F': doublePrecision= false; next++; break; case 'd': case 'D': doublePrecision= true; next++; break; default: throw new NumberFormatException(); } } // at this point, all the parsing is done // Step 7: handle mantissa of zero if (mantissa == 0) { return 0L; } // Step 8: normalize non-zero mantissa // mantissa is in right-hand mantissaBits // ensure that top bit (as opposed to hex digit) is 1 int scaleFactorCompensation= 0; long top= (mantissa >>> (mantissaBits - 4)); if ((top & 0x8) == 0) { mantissaBits--; scaleFactorCompensation++; if ((top & 0x4) == 0) { mantissaBits--; scaleFactorCompensation++; if ((top & 0x2) == 0) { mantissaBits--; scaleFactorCompensation++; } } } // Step 9: convert double literals to IEEE double long result= 0L; if (doublePrecision) { long fraction; if (mantissaBits > DOUBLE_PRECISION) { // more bits than we can keep int extraBits= mantissaBits - DOUBLE_PRECISION; // round to DOUBLE_PRECISION bits fraction= mantissa >>> (extraBits - 1); long lowBit= fraction & 0x1; fraction+= lowBit; fraction= fraction >>> 1; if ((fraction & (1L << DOUBLE_PRECISION)) != 0) { fraction= fraction >>> 1; scaleFactorCompensation-= 1; } } else { // less bits than the faction can hold - pad on right with 0s fraction= mantissa << (DOUBLE_PRECISION - mantissaBits); } int scaleFactor= 0; // how many bits to move '.' to before leading hex digit if (mantissaBits > 0) { if (leadingDigitPosition < binaryPointPosition) { // e.g., 0x80.0p0 has scaleFactor == +8 scaleFactor= 4 * (binaryPointPosition - leadingDigitPosition); // e.g., 0x10.0p0 has scaleFactorCompensation == +3 scaleFactor-= scaleFactorCompensation; } else { // e.g., 0x0.08p0 has scaleFactor == -4 scaleFactor= -4 * (leadingDigitPosition - binaryPointPosition - 1); // e.g., 0x0.01p0 has scaleFactorCompensation == +3 scaleFactor-= scaleFactorCompensation; } } int e= (exponentSign * exponent) + scaleFactor; if (e - 1 > MAX_DOUBLE_EXPONENT) { // overflow to +infinity result= Double.doubleToLongBits(Double.POSITIVE_INFINITY); } else if (e - 1 >= MIN_NORMALIZED_DOUBLE_EXPONENT) { // can be represented as a normalized double // the left most bit must be discarded (it's always a 1) long biasedExponent= e - 1 + DOUBLE_EXPONENT_BIAS; result= fraction & ~(1L << DOUBLE_FRACTION_WIDTH); result|= (biasedExponent << DOUBLE_EXPONENT_SHIFT); } else if (e - 1 > MIN_UNNORMALIZED_DOUBLE_EXPONENT) { // can be represented as an unnormalized double long biasedExponent= 0; result= fraction >>> (MIN_NORMALIZED_DOUBLE_EXPONENT - e + 1); result|= (biasedExponent << DOUBLE_EXPONENT_SHIFT); } else { // underflow - return Double.NaN result= Double.doubleToLongBits(Double.NaN); } return result; } // Step 10: convert float literals to IEEE single long fraction; if (mantissaBits > SINGLE_PRECISION) { // more bits than we can keep int extraBits= mantissaBits - SINGLE_PRECISION; // round to DOUBLE_PRECISION bits fraction= mantissa >>> (extraBits - 1); long lowBit= fraction & 0x1; fraction+= lowBit; fraction= fraction >>> 1; if ((fraction & (1L << SINGLE_PRECISION)) != 0) { fraction= fraction >>> 1; scaleFactorCompensation-= 1; } } else { // less bits than the faction can hold - pad on right with 0s fraction= mantissa << (SINGLE_PRECISION - mantissaBits); } int scaleFactor= 0; // how many bits to move '.' to before leading hex digit if (mantissaBits > 0) { if (leadingDigitPosition < binaryPointPosition) { // e.g., 0x80.0p0 has scaleFactor == +8 scaleFactor= 4 * (binaryPointPosition - leadingDigitPosition); // e.g., 0x10.0p0 has scaleFactorCompensation == +3 scaleFactor-= scaleFactorCompensation; } else { // e.g., 0x0.08p0 has scaleFactor == -4 scaleFactor= -4 * (leadingDigitPosition - binaryPointPosition - 1); // e.g., 0x0.01p0 has scaleFactorCompensation == +3 scaleFactor-= scaleFactorCompensation; } } int e= (exponentSign * exponent) + scaleFactor; if (e - 1 > MAX_SINGLE_EXPONENT) { // overflow to +infinity result= Float.floatToIntBits(Float.POSITIVE_INFINITY); } else if (e - 1 >= MIN_NORMALIZED_SINGLE_EXPONENT) { // can be represented as a normalized single // the left most bit must be discarded (it's always a 1) long biasedExponent= e - 1 + SINGLE_EXPONENT_BIAS; result= fraction & ~(1L << SINGLE_FRACTION_WIDTH); result|= (biasedExponent << SINGLE_EXPONENT_SHIFT); } else if (e - 1 > MIN_UNNORMALIZED_SINGLE_EXPONENT) { // can be represented as an unnormalized single long biasedExponent= 0; result= fraction >>> (MIN_NORMALIZED_SINGLE_EXPONENT - e + 1); result|= (biasedExponent << SINGLE_EXPONENT_SHIFT); } else { // underflow - return Float.NaN result= Float.floatToIntBits(Float.NaN); } return result; } }