org.eclipse.jdt.internal.compiler.util.FloatUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* 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
*/
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.
*
*
* @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.
*
*
* @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.
*
* @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;
}
}