test/java/lang/Float/ParseFloat.java

Print this page
rev 7487 : 7192954: Fix Float.parseFloat to round correctly and preserve monotonicity.
4396272: Parsing doubles fails to follow IEEE for largest decimal that should yield 0
7039391: Use Math.ulp in FloatingDecimal
Summary: Correct rounding and monotonicity problems in floats and doubles
Reviewed-by: martin
Contributed-by: Dmitry Nadezhin <dmitry.nadezhin@oracle.com>, Louis Wasserman <lowasser@google.com>

*** 21,41 **** * questions. */ /* * @test ! * @bug 4160406 4705734 4707389 * @summary Tests for Float.parseFloat method */ public class ParseFloat { private static void check(String val, float expected) { float n = Float.parseFloat(val); if (n != expected) ! throw new RuntimeException("Float.parseFloat failed. String:" + ! val + " Result:" + n); } private static void rudimentaryTest() { check(new String(""+Float.MIN_VALUE), Float.MIN_VALUE); check(new String(""+Float.MAX_VALUE), Float.MAX_VALUE); --- 21,129 ---- * questions. */ /* * @test ! * @bug 4160406 4705734 4707389 6358355 7032154 * @summary Tests for Float.parseFloat method */ + import java.math.BigDecimal; + import java.math.BigInteger; + public class ParseFloat { + private static final BigDecimal HALF = BigDecimal.valueOf(0.5); + + private static void fail(String val, float n) { + throw new RuntimeException("Float.parseFloat failed. String:" + + val + " Result:" + n); + } + + private static void check(String val) { + float n = Float.parseFloat(val); + boolean isNegativeN = n < 0 || n == 0 && 1/n < 0; + float na = Math.abs(n); + String s = val.trim().toLowerCase(); + switch (s.charAt(s.length() - 1)) { + case 'd': + case 'f': + s = s.substring(0, s.length() - 1); + break; + } + boolean isNegative = false; + if (s.charAt(0) == '+') { + s = s.substring(1); + } else if (s.charAt(0) == '-') { + s = s.substring(1); + isNegative = true; + } + if (s.equals("nan")) { + if (!Float.isNaN(n)) { + fail(val, n); + } + return; + } + if (Float.isNaN(n)) { + fail(val, n); + } + if (isNegativeN != isNegative) + fail(val, n); + if (s.equals("infinity")) { + if (na != Float.POSITIVE_INFINITY) { + fail(val, n); + } + return; + } + BigDecimal bd; + if (s.startsWith("0x")) { + s = s.substring(2); + int indP = s.indexOf('p'); + long exp = Long.parseLong(s.substring(indP + 1)); + int indD = s.indexOf('.'); + String significand; + if (indD >= 0) { + significand = s.substring(0, indD) + s.substring(indD + 1, indP); + exp -= 4*(indP - indD - 1); + } else { + significand = s.substring(0, indP); + } + bd = new BigDecimal(new BigInteger(significand, 16)); + if (exp >= 0) { + bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp)); + } else { + bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp)); + } + } else { + bd = new BigDecimal(s); + } + BigDecimal l, u; + if (Float.isInfinite(na)) { + l = new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)); + u = null; + } else { + l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(-Math.nextUp(-na))).multiply(HALF)); + u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF)); + } + int cmpL = bd.compareTo(l); + int cmpU = u != null ? bd.compareTo(u) : -1; + if ((Float.floatToIntBits(n) & 1) != 0) { + if (cmpL <= 0 || cmpU >= 0) { + fail(val, n); + } + } else { + if (cmpL < 0 || cmpU > 0) { + fail(val, n); + } + } + } + private static void check(String val, float expected) { float n = Float.parseFloat(val); if (n != expected) ! fail(val, n); ! check(val); } private static void rudimentaryTest() { check(new String(""+Float.MIN_VALUE), Float.MIN_VALUE); check(new String(""+Float.MAX_VALUE), Float.MAX_VALUE);
*** 45,54 **** --- 133,153 ---- check("10.01", (float) 10.01); check("-10", (float) -10.0); check("-10.00", (float) -10.0); check("-10.01", (float) -10.01); + + // bug 6358355 + check("144115196665790480", 0x1.000002p57f); + check("144115196665790481", 0x1.000002p57f); + check("0.050000002607703203", 0.05f); + check("0.050000002607703204", 0.05f); + check("0.050000002607703205", 0.05f); + check("0.050000002607703206", 0.05f); + check("0.050000002607703207", 0.05f); + check("0.050000002607703208", 0.05f); + check("0.050000002607703209", 0.050000004f); } static String badStrings[] = { "", "+",
*** 180,189 **** --- 279,289 ---- for(int i = 0; i < input.length; i++) { double d; try { d = Float.parseFloat(input[i]); + check(input[i]); } catch (NumberFormatException e) { if (! exceptionalInput) { throw new RuntimeException("Float.parseFloat rejected " + "good string `" + input[i] +
*** 197,210 **** --- 297,330 ---- "'."); } } } + /** + * For each power of two, test at boundaries of + * region that should convert to that value. + */ + private static void testPowers() { + for(int i = -149; i <= +127; i++) { + float f = Math.scalb(1.0f, i); + BigDecimal f_BD = new BigDecimal(f); + + BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF)); + BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF)); + + check(lowerBound.toString()); + check(upperBound.toString()); + } + check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString()); + } + public static void main(String[] args) throws Exception { rudimentaryTest(); testParsing(goodStrings, false); testParsing(paddedGoodStrings, false); testParsing(badStrings, true); testParsing(paddedBadStrings, true); + + testPowers(); } }