• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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