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