Copyright (c) 2004, 2011 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2004, 2011 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.jdt.internal.compiler.util;
Internal utility for declaring with hexadecimal double and float literals.
Since:3.1
/** * Internal utility for declaring 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 syntactically 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.

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.

Params:
  • source – source string containing single precision hexadecimal floating-point literal
Returns: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
/** * Returns the float value corresponding to the given * hexadecimal floating-point single precision literal. * The literal must be syntactically 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.

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.

Params:
  • source – source string containing double precision hexadecimal floating-point literal
Returns: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
/** * 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 syntactically correct. It must not include either leading or trailing whitespace or a sign.
Params:
  • source – source string containing hexadecimal floating-point literal
Throws:
Returns: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
/** * 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 syntactically 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; } }