1 /* 2 * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 4160406 4705734 4707389 6358355 7032154 27 * @summary Tests for Float.parseFloat method 28 */ 29 30 package test.java.lang.Float; 31 32 import java.math.BigDecimal; 33 import java.math.BigInteger; 34 35 public class ParseFloat { 36 37 private static final BigDecimal HALF = BigDecimal.valueOf(0.5); 38 fail(String val, float n)39 private static void fail(String val, float n) { 40 throw new RuntimeException("Float.parseFloat failed. String:" + 41 val + " Result:" + n); 42 } 43 check(String val)44 private static void check(String val) { 45 float n = Float.parseFloat(val); 46 boolean isNegativeN = n < 0 || n == 0 && 1/n < 0; 47 float na = Math.abs(n); 48 String s = val.trim().toLowerCase(); 49 switch (s.charAt(s.length() - 1)) { 50 case 'd': 51 case 'f': 52 s = s.substring(0, s.length() - 1); 53 break; 54 } 55 boolean isNegative = false; 56 if (s.charAt(0) == '+') { 57 s = s.substring(1); 58 } else if (s.charAt(0) == '-') { 59 s = s.substring(1); 60 isNegative = true; 61 } 62 if (s.equals("nan")) { 63 if (!Float.isNaN(n)) { 64 fail(val, n); 65 } 66 return; 67 } 68 if (Float.isNaN(n)) { 69 fail(val, n); 70 } 71 if (isNegativeN != isNegative) 72 fail(val, n); 73 if (s.equals("infinity")) { 74 if (na != Float.POSITIVE_INFINITY) { 75 fail(val, n); 76 } 77 return; 78 } 79 BigDecimal bd; 80 if (s.startsWith("0x")) { 81 s = s.substring(2); 82 int indP = s.indexOf('p'); 83 long exp = Long.parseLong(s.substring(indP + 1)); 84 int indD = s.indexOf('.'); 85 String significand; 86 if (indD >= 0) { 87 significand = s.substring(0, indD) + s.substring(indD + 1, indP); 88 exp -= 4*(indP - indD - 1); 89 } else { 90 significand = s.substring(0, indP); 91 } 92 bd = new BigDecimal(new BigInteger(significand, 16)); 93 if (exp >= 0) { 94 bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp)); 95 } else { 96 bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp)); 97 } 98 } else { 99 bd = new BigDecimal(s); 100 } 101 BigDecimal l, u; 102 if (Float.isInfinite(na)) { 103 l = new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)); 104 u = null; 105 } else { 106 l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(-Math.nextUp(-na))).multiply(HALF)); 107 u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF)); 108 } 109 int cmpL = bd.compareTo(l); 110 int cmpU = u != null ? bd.compareTo(u) : -1; 111 if ((Float.floatToIntBits(n) & 1) != 0) { 112 if (cmpL <= 0 || cmpU >= 0) { 113 fail(val, n); 114 } 115 } else { 116 if (cmpL < 0 || cmpU > 0) { 117 fail(val, n); 118 } 119 } 120 } 121 check(String val, float expected)122 private static void check(String val, float expected) { 123 float n = Float.parseFloat(val); 124 if (n != expected) 125 fail(val, n); 126 check(val); 127 } 128 rudimentaryTest()129 private static void rudimentaryTest() { 130 check(new String(""+Float.MIN_VALUE), Float.MIN_VALUE); 131 check(new String(""+Float.MAX_VALUE), Float.MAX_VALUE); 132 133 check("10", (float) 10.0); 134 check("10.0", (float) 10.0); 135 check("10.01", (float) 10.01); 136 137 check("-10", (float) -10.0); 138 check("-10.00", (float) -10.0); 139 check("-10.01", (float) -10.01); 140 141 // bug 6358355 142 check("144115196665790480", 0x1.000002p57f); 143 check("144115196665790481", 0x1.000002p57f); 144 check("0.050000002607703203", 0.05f); 145 check("0.050000002607703204", 0.05f); 146 check("0.050000002607703205", 0.05f); 147 check("0.050000002607703206", 0.05f); 148 check("0.050000002607703207", 0.05f); 149 check("0.050000002607703208", 0.05f); 150 check("0.050000002607703209", 0.050000004f); 151 } 152 153 static String badStrings[] = { 154 "", 155 "+", 156 "-", 157 "+e", 158 "-e", 159 "+e170", 160 "-e170", 161 162 // Make sure intermediate white space is not deleted. 163 "1234 e10", 164 "-1234 e10", 165 166 // Control characters in the interior of a string are not legal 167 "1\u0007e1", 168 "1e\u00071", 169 170 // NaN and infinity can't have trailing type suffices or exponents 171 "NaNf", 172 "NaNF", 173 "NaNd", 174 "NaND", 175 "-NaNf", 176 "-NaNF", 177 "-NaNd", 178 "-NaND", 179 "+NaNf", 180 "+NaNF", 181 "+NaNd", 182 "+NaND", 183 "Infinityf", 184 "InfinityF", 185 "Infinityd", 186 "InfinityD", 187 "-Infinityf", 188 "-InfinityF", 189 "-Infinityd", 190 "-InfinityD", 191 "+Infinityf", 192 "+InfinityF", 193 "+Infinityd", 194 "+InfinityD", 195 196 "NaNe10", 197 "-NaNe10", 198 "+NaNe10", 199 "Infinitye10", 200 "-Infinitye10", 201 "+Infinitye10", 202 203 // Non-ASCII digits are not recognized 204 "\u0661e\u0661", // 1e1 in Arabic-Indic digits 205 "\u06F1e\u06F1", // 1e1 in Extended Arabic-Indic digits 206 "\u0967e\u0967" // 1e1 in Devanagari digits 207 }; 208 209 static String goodStrings[] = { 210 "NaN", 211 "+NaN", 212 "-NaN", 213 "Infinity", 214 "+Infinity", 215 "-Infinity", 216 "1.1e-23f", 217 ".1e-23f", 218 "1e-23", 219 "1f", 220 "1", 221 "2", 222 "1234", 223 "-1234", 224 "+1234", 225 "2147483647", // Integer.MAX_VALUE 226 "2147483648", 227 "-2147483648", // Integer.MIN_VALUE 228 "-2147483649", 229 230 "16777215", 231 "16777216", // 2^24 232 "16777217", 233 234 "-16777215", 235 "-16777216", // -2^24 236 "-16777217", 237 238 "9007199254740991", 239 "9007199254740992", // 2^53 240 "9007199254740993", 241 242 "-9007199254740991", 243 "-9007199254740992", // -2^53 244 "-9007199254740993", 245 246 "9223372036854775807", 247 "9223372036854775808", // Long.MAX_VALUE 248 "9223372036854775809", 249 250 "-9223372036854775808", 251 "-9223372036854775809", // Long.MIN_VALUE 252 "-9223372036854775810" 253 }; 254 255 static String paddedBadStrings[]; 256 static String paddedGoodStrings[]; 257 static { 258 String pad = " \t\n\r\f\u0001\u000b\u001f"; 259 paddedBadStrings = new String[badStrings.length]; 260 for(int i = 0 ; i < badStrings.length; i++) 261 paddedBadStrings[i] = pad + badStrings[i] + pad; 262 263 paddedGoodStrings = new String[goodStrings.length]; 264 for(int i = 0 ; i < goodStrings.length; i++) 265 paddedGoodStrings[i] = pad + goodStrings[i] + pad; 266 267 } 268 269 /* 270 * Throws an exception if <code>Input</code> is 271 * <code>exceptionalInput</code> and {@link Float.parseFloat 272 * parseFloat} does <em>not</em> throw an exception or if 273 * <code>Input</code> is not <code>exceptionalInput</code> and 274 * <code>parseFloat</code> throws an exception. This method does 275 * not attempt to test whether the string is converted to the 276 * proper value; just whether the input is accepted appropriately 277 * or not. 278 */ testParsing(String [] input, boolean exceptionalInput)279 private static void testParsing(String [] input, 280 boolean exceptionalInput) { 281 for (String s : input) { 282 try { 283 Float.parseFloat(s); 284 check(s); 285 } catch (NumberFormatException e) { 286 if (!exceptionalInput) { 287 throw new RuntimeException("Float.parseFloat rejected " + 288 "good string `" + s + 289 "'."); 290 } 291 continue; 292 } 293 if (exceptionalInput) { 294 throw new RuntimeException("Float.parseFloat accepted " + 295 "bad string `" + s + 296 "'."); 297 } 298 } 299 } 300 301 /** 302 * For each power of two, test at boundaries of 303 * region that should convert to that value. 304 */ testPowers()305 private static void testPowers() { 306 for(int i = -149; i <= +127; i++) { 307 float f = Math.scalb(1.0f, i); 308 BigDecimal f_BD = new BigDecimal(f); 309 310 BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF)); 311 BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF)); 312 313 check(lowerBound.toString()); 314 check(upperBound.toString()); 315 } 316 check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString()); 317 } 318 main(String[] args)319 public static void main(String[] args) throws Exception { 320 rudimentaryTest(); 321 322 testParsing(goodStrings, false); 323 testParsing(paddedGoodStrings, false); 324 testParsing(badStrings, true); 325 testParsing(paddedBadStrings, true); 326 327 testPowers(); 328 } 329 } 330