--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-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;
+ }
+}