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 
 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 }