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