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