Index: compiler/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java,v retrieving revision 1.10 diff -u -r1.10 DoubleLiteral.java --- compiler/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java 21 Sep 2004 17:41:33 -0000 1.10 +++ compiler/org/eclipse/jdt/internal/compiler/ast/DoubleLiteral.java 27 Sep 2004 20:52:29 -0000 @@ -10,11 +10,12 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.codegen.*; import org.eclipse.jdt.internal.compiler.lookup.*; -import org.eclipse.jdt.internal.compiler.util.Util; +import org.eclipse.jdt.internal.compiler.util.FloatUtil; public class DoubleLiteral extends NumberLiteral { double value; @@ -22,44 +23,66 @@ super(token, s, e); } public void computeConstant() { - //the source is correctly formated so the exception should never occurs + if (CharOperation.indexOf('x', source) >= 0 + && CharOperation.indexOf('p', source) >= 0) { + // hex floating point literal + try { + double v = FloatUtil.valueOfHexDoubleLiteral(source); + if (v == Double.POSITIVE_INFINITY) { + // error: the number is too large to represent + return; + } + if (Double.isNaN(v)) { + // error: the number is too small to represent + return; + } + value = v; + constant = Constant.fromValue(v); + } catch (NumberFormatException e) { + // the source is correctly formated so the exception should never occurs + return; + } + } + + // non-hexadecimal floating point literals Double computedValue; try { computedValue = Double.valueOf(String.valueOf(source)); } catch (NumberFormatException e) { - /* - * this can happen if this is an hexadecimal floating-point literal and the libraries used - * are < 1.5 - */ - computedValue = new Double(Util.getFloatingPoint(source)); + // the source is correctly formated so the exception should never occurs + return; } final double doubleValue = computedValue.doubleValue(); - if (doubleValue > Double.MAX_VALUE) - return; //may be Infinity - if (doubleValue < Double.MIN_VALUE) { //only a true 0 can be made of zeros - //2.00000000000000000e-324 is illegal .... + if (doubleValue > Double.MAX_VALUE) { + // error: the number is too large to represent + return; + } + if (doubleValue < Double.MIN_VALUE) { + // see 1F6IGUU + // a true 0 only has '0' and '.' in mantissa + // 1.0e-5000d is non-zero, but underflows to 0 label : for (int i = 0; i < source.length; i++) { //it is welled formated so just test against '0' and potential . D d switch (source[i]) { case '0' : case '.' : - case 'd' : - case 'D' : - case 'x' : - case 'X' : break; case 'e' : case 'E' : - case 'p' : - case 'P' : - break label; //exposant are valid....! + // starting the exponent - mantissa is all zero + break label; + case 'f' : + case 'F' : + // no exponent - mantissa is all zero + break label; default : + // error: the number is too small to represent return; } } - } //error - - constant = Constant.fromValue(value = doubleValue); + } + value = doubleValue; + constant = Constant.fromValue(value); } /** * Code generation for the double literak Index: compiler/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java,v retrieving revision 1.10 diff -u -r1.10 FloatLiteral.java --- compiler/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java 21 Sep 2004 17:41:33 -0000 1.10 +++ compiler/org/eclipse/jdt/internal/compiler/ast/FloatLiteral.java 27 Sep 2004 20:52:29 -0000 @@ -10,12 +10,13 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; -import org.eclipse.jdt.internal.compiler.util.Util; +import org.eclipse.jdt.internal.compiler.util.FloatUtil; public class FloatLiteral extends NumberLiteral { float value; @@ -24,45 +25,66 @@ super(token, s, e); } public void computeConstant() { - //the source is correctly formated so the exception should never occurs + if (CharOperation.indexOf('x', source) >= 0 + && CharOperation.indexOf('p', source) >= 0) { + // hex floating point literal + try { + float v = FloatUtil.valueOfHexFloatLiteral(source); + if (v == Float.POSITIVE_INFINITY) { + // error: the number is too large to represent + return; + } + if (Float.isNaN(v)) { + // error: the number is too small to represent + return; + } + value = v; + constant = Constant.fromValue(v); + } catch (NumberFormatException e) { + // the source is correctly formated so the exception should never occurs + return; + } + } + + // non-hexadecimal floating point literals Float computedValue; try { computedValue = Float.valueOf(String.valueOf(source)); } catch (NumberFormatException e) { - /* - * this can happen if this is an hexadecimal floating-point literal and the libraries used - * are < 1.5 - */ - computedValue = new Float(Util.getFloatingPoint(source)); + // the source is correctly formated so the exception should never occurs + return; } - if (computedValue.doubleValue() > Float.MAX_VALUE) { - return; //may be Infinity + final float floatValue = computedValue.floatValue(); + if (floatValue > Float.MAX_VALUE) { + // error: the number is too large to represent + return; } - if (computedValue.floatValue() < Float_MIN_VALUE) { + if (floatValue < Float_MIN_VALUE) { // see 1F6IGUU - //only a true 0 can be made of zeros - //1.00000000e-46f is illegal .... + // a true 0 only has '0' and '.' in mantissa + // 1.0e-5000f is non-zero, but underflows to 0 label : for (int i = 0; i < source.length; i++) { switch (source[i]) { case '.' : - case 'f' : - case 'F' : case '0' : - case 'x' : - case 'X' : break; case 'e' : case 'E' : - case 'p' : - case 'P' : - break label; //exposant are valid !.... + // starting the exponent - mantissa is all zero + break label; + case 'd' : + case 'D' : + // no exponent - mantissa is all zero + break label; default : - return; //error + // error: the number is too small to represent + return; } } } - constant = Constant.fromValue(value = computedValue.floatValue()); + value = floatValue; + constant = Constant.fromValue(value); } /** * Code generation for float literal Index: compiler/org/eclipse/jdt/internal/compiler/util/Util.java =================================================================== RCS file: /data/cvs/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Util.java,v retrieving revision 1.41 diff -u -r1.41 Util.java --- compiler/org/eclipse/jdt/internal/compiler/util/Util.java 21 Sep 2004 17:41:33 -0000 1.41 +++ compiler/org/eclipse/jdt/internal/compiler/util/Util.java 27 Sep 2004 20:52:29 -0000 @@ -520,220 +520,4 @@ return Boolean.FALSE; } } - - /** - * Returns the double value corresponding to the hexadecimal floating-point literal - * @return the double value corresponding to the hexadecimal floating-point literal - */ - public static double getFloatingPoint(char[] source) { - int length = source.length; - long hexValue = 0; - int i = 2; - loop: while (true) { - switch(source[i]) { - case '0' : - hexValue <<= 4; - break; - case '1' : - hexValue <<= 4; - hexValue ++; - break; - case '2' : - hexValue <<= 4; - hexValue += 2; - break; - case '3' : - hexValue <<= 4; - hexValue += 3; - break; - case '4' : - hexValue <<= 4; - hexValue += 4; - break; - case '5' : - hexValue <<= 4; - hexValue += 5; - break; - case '6' : - hexValue <<= 4; - hexValue += 6; - break; - case '7' : - hexValue <<= 4; - hexValue += 7; - break; - case '8' : - hexValue <<= 4; - hexValue += 8; - break; - case '9' : - hexValue <<= 4; - hexValue += 9; - break; - case 'a' : - case 'A' : - hexValue <<= 4; - hexValue += 10; - break; - case 'b' : - case 'B' : - hexValue <<= 4; - hexValue += 11; - break; - case 'c' : - case 'C' : - hexValue <<= 4; - hexValue += 12; - break; - case 'd' : - case 'D' : - hexValue <<= 4; - hexValue += 13; - break; - case 'e' : - case 'E' : - hexValue <<= 4; - hexValue += 14; - break; - case 'F' : - case 'f' : - hexValue <<= 4; - hexValue += 15; - break; - default: - break loop; - } - i++; - } - double decimalsValue = 0.0; - if (source[i] == '.') { - int index = 1; - i++; - loop2 : while (true) { - switch(source[i]) { - case '1' : - decimalsValue += 1.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '2' : - decimalsValue += 2.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '3' : - decimalsValue += 3.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '4' : - decimalsValue += 4.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '5' : - decimalsValue += 5.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '6' : - decimalsValue += 6.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '7' : - decimalsValue += 7.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '8' : - decimalsValue += 8.0 / ((2 << 3) << (4 * (index - 1))); - break; - case '9' : - decimalsValue += 9.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'a' : - case 'A' : - decimalsValue += 10.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'b' : - case 'B' : - decimalsValue += 11.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'c' : - case 'C' : - decimalsValue += 12.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'd' : - case 'D' : - decimalsValue += 13.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'e' : - case 'E' : - decimalsValue += 14.0 / ((2 << 3) << (4 * (index - 1))); - break; - case 'F' : - case 'f' : - decimalsValue += 15.0 / ((2 << 3) << (4 * (index - 1))); - break; - default: - break loop2; - } - i++; - index++; - } - } - i++; // read p or P - boolean isNegative = false; - switch(source[i]) { - case '-' : - isNegative = true; - i++; - break; - case '+' : - i++; - break; - } - int exponentValue = 0; - loop3: while (true) { - switch(source[i]) { - case '0' : - exponentValue *= 10; - break; - case '1' : - exponentValue *= 10; - exponentValue++; - break; - case '2' : - exponentValue *= 10; - exponentValue += 2; - break; - case '3' : - exponentValue *= 10; - exponentValue += 3; - break; - case '4' : - exponentValue *= 10; - exponentValue += 4; - break; - case '5' : - exponentValue *= 10; - exponentValue += 5; - break; - case '6' : - exponentValue *= 10; - exponentValue += 6; - break; - case '7' : - exponentValue *= 10; - exponentValue += 7; - break; - case '8' : - exponentValue *= 10; - exponentValue += 8; - break; - case '9' : - exponentValue *= 10; - exponentValue += 9; - break; - default: - break loop3; - } - i++; - if (i >= length) { - break loop3; - } - } - if (exponentValue == 0) { - return hexValue + decimalsValue; - } - exponentValue--; - return (hexValue + decimalsValue) * (isNegative ? 1.0 / (2 << exponentValue) : 2 << exponentValue); - } } Index: compiler/org/eclipse/jdt/internal/compiler/util/FloatUtil.java =================================================================== RCS file: compiler/org/eclipse/jdt/internal/compiler/util/FloatUtil.java diff -N compiler/org/eclipse/jdt/internal/compiler/util/FloatUtil.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ compiler/org/eclipse/jdt/internal/compiler/util/FloatUtil.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,421 @@ +/******************************************************************************* + * 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. + *

+ * 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; + } +}