org.eclipse.jdt.internal.compiler.util.FloatUtil Maven / Gradle / Ivy
/*******************************************************************************
* 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.
*
* 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 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;
}
}