1 /******************************************************************************
2  *
3  * Project:  CPL - Common Portability Library
4  * Purpose:  Functions to convert ASCII string to floating point number.
5  * Author:   Andrey Kiselev, dron@ak4719.spb.edu.
6  *
7  ******************************************************************************
8  * Copyright (c) 2006, Andrey Kiselev
9  * Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_port.h"
31 #include "cpl_conv.h"
32 
33 #include <cerrno>
34 #include <clocale>
35 #include <cstring>
36 #include <cstdlib>
37 #include <limits>
38 
39 #include "cpl_config.h"
40 
41 CPL_CVSID("$Id: cpl_strtod.cpp 2750a2a20b7024e64509cdbfc50fdd6b1a186550 2020-05-25 14:36:21 +0200 Even Rouault $")
42 
43 // XXX: with GCC 2.95 strtof() function is only available when in c99 mode.
44 // Fix it here not touching the compiler options.
45 #if defined(HAVE_STRTOF) && !HAVE_DECL_STRTOF
46 extern "C" {
47 extern float strtof(const char *nptr, char **endptr);
48 }
49 #endif
50 
51 /************************************************************************/
52 /*                            CPLAtofDelim()                            */
53 /************************************************************************/
54 
55 /**
56  * Converts ASCII string to floating point number.
57  *
58  * This function converts the initial portion of the string pointed to
59  * by nptr to double floating point representation. The behavior is the
60  * same as
61  *
62  *   CPLStrtodDelim(nptr, (char **)NULL, point);
63  *
64  * This function does the same as standard atof(3), but does not take locale
65  * in account. Instead of locale defined decimal delimiter you can specify
66  * your own one. Also see notes for CPLAtof() function.
67  *
68  * @param nptr Pointer to string to convert.
69  * @param point Decimal delimiter.
70  *
71  * @return Converted value, if any.
72  */
CPLAtofDelim(const char * nptr,char point)73 double CPLAtofDelim(const char *nptr, char point)
74 {
75     return CPLStrtodDelim(nptr, nullptr, point);
76 }
77 
78 /************************************************************************/
79 /*                              CPLAtof()                               */
80 /************************************************************************/
81 
82 /**
83  * Converts ASCII string to floating point number.
84  *
85  * This function converts the initial portion of the string pointed to
86  * by nptr to double floating point representation. The behavior is the
87  * same as
88  *
89  *   CPLStrtod(nptr, (char **)NULL);
90  *
91  * This function does the same as standard atof(3), but does not take
92  * locale in account. That means, the decimal delimiter is always '.'
93  * (decimal point). Use CPLAtofDelim() function if you want to specify
94  * custom delimiter.
95  *
96  * IMPORTANT NOTE:
97  *
98  * Existence of this function does not mean you should always use it.  Sometimes
99  * you should use standard locale aware atof(3) and its family. When you need to
100  * process the user's input (for example, command line parameters) use atof(3),
101  * because the user works in a localized environment and the user's input will
102  * be done according to the locale set. In particular that means we should not
103  * make assumptions about character used as decimal delimiter, it can be either
104  * "." or ",".
105  *
106  * But when you are parsing some ASCII file in predefined format, you most
107  * likely need CPLAtof(), because such files distributed across the systems
108  * with different locales and floating point representation should be
109  * considered as a part of file format. If the format uses "." as a delimiter
110  * the same character must be used when parsing number regardless of actual
111  * locale setting.
112  *
113  * @param nptr Pointer to string to convert.
114  *
115  * @return Converted value, if any.
116  */
CPLAtof(const char * nptr)117 double CPLAtof(const char *nptr)
118 {
119     return CPLStrtod(nptr, nullptr);
120 }
121 
122 /************************************************************************/
123 /*                              CPLAtofM()                              */
124 /************************************************************************/
125 
126 /**
127  * Converts ASCII string to floating point number using any numeric locale.
128  *
129  * This function converts the initial portion of the string pointed to
130  * by nptr to double floating point representation. This function does the
131  * same as standard atof(), but it allows a variety of locale representations.
132  * That is it supports numeric values with either a comma or a period for
133  * the decimal delimiter.
134  *
135  * PS. The M stands for Multi-lingual.
136  *
137  * @param nptr The string to convert.
138  *
139  * @return Converted value, if any.  Zero on failure.
140  */
141 
CPLAtofM(const char * nptr)142 double CPLAtofM( const char *nptr )
143 
144 {
145     const int nMaxSearch = 50;
146 
147     for( int i = 0; i < nMaxSearch; i++ )
148     {
149         if( nptr[i] == ',' )
150             return CPLStrtodDelim( nptr, nullptr, ',' );
151         if( nptr[i] == '.' || nptr[i] == '\0' )
152             return CPLStrtodDelim( nptr, nullptr, '.' );
153     }
154 
155     return CPLStrtodDelim( nptr, nullptr, '.' );
156 }
157 
158 /************************************************************************/
159 /*                      CPLReplacePointByLocalePoint()                  */
160 /************************************************************************/
161 
162 /* Return a newly allocated variable if substitution was done, or NULL
163  * otherwise.
164  */
CPLReplacePointByLocalePoint(const char * pszNumber,char point)165 static char* CPLReplacePointByLocalePoint( const char* pszNumber, char point )
166 {
167 #if defined(__ANDROID__)
168     static char byPoint = 0;
169     if( byPoint == 0 )
170     {
171         char szBuf[16] = {};
172         snprintf(szBuf, sizeof(szBuf), "%.1f", 1.0);
173         byPoint = szBuf[1];
174     }
175     if( point != byPoint )
176     {
177         const char* pszPoint = strchr(pszNumber, point);
178         if( pszPoint )
179         {
180             char* pszNew = CPLStrdup(pszNumber);
181             pszNew[pszPoint - pszNumber] = byPoint;
182             return pszNew;
183         }
184     }
185 #else  // ndef __ANDROID__
186     struct lconv *poLconv = localeconv();
187     if( poLconv
188         && poLconv->decimal_point
189         && poLconv->decimal_point[0] != '\0' )
190     {
191         char byPoint = poLconv->decimal_point[0];
192 
193         if( point != byPoint )
194         {
195             const char* pszLocalePoint = strchr(pszNumber, byPoint);
196             const char* pszPoint = strchr(pszNumber, point);
197             if( pszPoint || pszLocalePoint )
198             {
199                 char* pszNew = CPLStrdup(pszNumber);
200                 if( pszLocalePoint )
201                     pszNew[pszLocalePoint - pszNumber] = ' ';
202                 if( pszPoint )
203                     pszNew[pszPoint - pszNumber] = byPoint;
204                 return pszNew;
205             }
206         }
207     }
208 #endif  // __ANDROID__
209 
210     return nullptr;
211 }
212 
213 /************************************************************************/
214 /*                          CPLStrtodDelim()                            */
215 /************************************************************************/
216 
217 /**
218  * Converts ASCII string to floating point number using specified delimiter.
219  *
220  * This function converts the initial portion of the string pointed to
221  * by nptr to double floating point representation. This function does the
222  * same as standard strtod(3), but does not take locale in account. Instead of
223  * locale defined decimal delimiter you can specify your own one. Also see
224  * notes for CPLAtof() function.
225  *
226  * @param nptr Pointer to string to convert.
227  * @param endptr If is not NULL, a pointer to the character after the last
228  * character used in the conversion is stored in the location referenced
229  * by endptr.
230  * @param point Decimal delimiter.
231  *
232  * @return Converted value, if any.
233  */
CPLStrtodDelim(const char * nptr,char ** endptr,char point)234 double CPLStrtodDelim(const char *nptr, char **endptr, char point)
235 {
236     while( *nptr == ' ' )
237         nptr++;
238 
239     if( nptr[0] == '-' )
240     {
241         if( STARTS_WITH(nptr, "-1.#QNAN") ||
242             STARTS_WITH(nptr, "-1.#IND") )
243         {
244             if( endptr ) *endptr = const_cast<char *>(nptr) + strlen(nptr);
245             // While it is possible on some platforms to flip the sign
246             // of NAN to negative, this function will always return a positive
247             // quiet (non-signalling) NaN.
248             return std::numeric_limits<double>::quiet_NaN();
249         }
250 
251         if( strcmp(nptr, "-inf") == 0 ||
252             STARTS_WITH_CI(nptr, "-1.#INF") )
253         {
254             if( endptr ) *endptr = const_cast<char *>(nptr) + strlen(nptr);
255             return -std::numeric_limits<double>::infinity();
256         }
257     }
258     else if( nptr[0] == '1' )
259     {
260         if( STARTS_WITH(nptr, "1.#QNAN") ||
261             STARTS_WITH(nptr, "1.#SNAN") )
262         {
263             if( endptr ) *endptr = const_cast<char *>(nptr) + strlen(nptr);
264             return std::numeric_limits<double>::quiet_NaN();
265         }
266         if( STARTS_WITH_CI(nptr, "1.#INF") )
267         {
268             if( endptr ) *endptr = const_cast<char*>(nptr) + strlen(nptr);
269             return std::numeric_limits<double>::infinity();
270         }
271     }
272     else if( nptr[0] == 'i' && strcmp(nptr, "inf") == 0 )
273     {
274         if( endptr ) *endptr = const_cast<char *>(nptr) + strlen(nptr);
275         return std::numeric_limits<double>::infinity();
276     }
277     else if( nptr[0] == 'n' && strcmp(nptr, "nan") == 0 )
278     {
279         if( endptr ) *endptr = const_cast<char *>(nptr) + strlen(nptr);
280         return std::numeric_limits<double>::quiet_NaN();
281     }
282 
283 /* -------------------------------------------------------------------- */
284 /*  We are implementing a simple method here: copy the input string     */
285 /*  into the temporary buffer, replace the specified decimal delimiter  */
286 /*  with the one, taken from locale settings and use standard strtod()  */
287 /*  on that buffer.                                                     */
288 /* -------------------------------------------------------------------- */
289     char* pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
290     const char* pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
291 
292     const double dfValue = strtod( pszNumber, endptr );
293     const int nError = errno;
294 
295     if( endptr )
296         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
297 
298     if( pszNewNumberOrNull )
299         CPLFree( pszNewNumberOrNull );
300 
301     errno = nError;
302     return dfValue;
303 }
304 
305 /************************************************************************/
306 /*                             CPLStrtod()                              */
307 /************************************************************************/
308 
309 /**
310  * Converts ASCII string to floating point number.
311  *
312  * This function converts the initial portion of the string pointed to
313  * by nptr to double floating point representation. This function does the
314  * same as standard strtod(3), but does not take locale in account. That
315  * means, the decimal delimiter is always '.' (decimal point). Use
316  * CPLStrtodDelim() function if you want to specify custom delimiter. Also
317  * see notes for CPLAtof() function.
318  *
319  * @param nptr Pointer to string to convert.
320  * @param endptr If is not NULL, a pointer to the character after the last
321  * character used in the conversion is stored in the location referenced
322  * by endptr.
323  *
324  * @return Converted value, if any.
325  */
CPLStrtod(const char * nptr,char ** endptr)326 double CPLStrtod(const char *nptr, char **endptr)
327 {
328     return CPLStrtodDelim(nptr, endptr, '.');
329 }
330 
331 /************************************************************************/
332 /*                          CPLStrtofDelim()                            */
333 /************************************************************************/
334 
335 /**
336  * Converts ASCII string to floating point number using specified delimiter.
337  *
338  * This function converts the initial portion of the string pointed to
339  * by nptr to single floating point representation. This function does the
340  * same as standard strtof(3), but does not take locale in account. Instead of
341  * locale defined decimal delimiter you can specify your own one. Also see
342  * notes for CPLAtof() function.
343  *
344  * @param nptr Pointer to string to convert.
345  * @param endptr If is not NULL, a pointer to the character after the last
346  * character used in the conversion is stored in the location referenced
347  * by endptr.
348  * @param point Decimal delimiter.
349  *
350  * @return Converted value, if any.
351  */
CPLStrtofDelim(const char * nptr,char ** endptr,char point)352 float CPLStrtofDelim(const char *nptr, char **endptr, char point)
353 {
354 #if defined(HAVE_STRTOF)
355 /* -------------------------------------------------------------------- */
356 /*  We are implementing a simple method here: copy the input string     */
357 /*  into the temporary buffer, replace the specified decimal delimiter  */
358 /*  with the one, taken from locale settings and use standard strtof()  */
359 /*  on that buffer.                                                     */
360 /* -------------------------------------------------------------------- */
361     char * const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
362     const char* pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
363     double dfValue = strtof( pszNumber, endptr );
364     const int nError = errno;
365 
366     if( endptr )
367         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
368 
369     if( pszNewNumberOrNull )
370         CPLFree( pszNewNumberOrNull );
371 
372     errno = nError;
373     return static_cast<float>(dfValue);
374 
375 #else
376 
377     return static_cast<float>( CPLStrtodDelim(nptr, endptr, point) );
378 
379 #endif  // HAVE_STRTOF
380 }
381 
382 /************************************************************************/
383 /*                             CPLStrtof()                              */
384 /************************************************************************/
385 
386 /**
387  * Converts ASCII string to floating point number.
388  *
389  * This function converts the initial portion of the string pointed to
390  * by nptr to single floating point representation. This function does the
391  * same as standard strtof(3), but does not take locale in account. That
392  * means, the decimal delimiter is always '.' (decimal point). Use
393  * CPLStrtofDelim() function if you want to specify custom delimiter. Also
394  * see notes for CPLAtof() function.
395  *
396  * @param nptr Pointer to string to convert.
397  * @param endptr If is not NULL, a pointer to the character after the last
398  * character used in the conversion is stored in the location referenced
399  * by endptr.
400  *
401  * @return Converted value, if any.
402  */
CPLStrtof(const char * nptr,char ** endptr)403 float CPLStrtof(const char *nptr, char **endptr)
404 {
405     return CPLStrtofDelim(nptr, endptr, '.');
406 }
407