Line data Source code
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 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_conv.h"
16 :
17 : #include <cerrno>
18 : #include <clocale>
19 : #include <cstring>
20 : #include <cstdlib>
21 : #include <limits>
22 :
23 : // Coverity complains about CPLAtof(CPLGetConfigOption(...)) causing
24 : // a "untrusted loop bound" in the loop "Find a reasonable position for the end
25 : // of the string to provide to fast_float"
26 : #ifndef __COVERITY__
27 : #define USE_FAST_FLOAT
28 : #endif
29 :
30 : #ifdef USE_FAST_FLOAT
31 : #include "include_fast_float.h"
32 : #endif
33 :
34 : #include "cpl_config.h"
35 :
36 : /************************************************************************/
37 : /* CPLAtofDelim() */
38 : /************************************************************************/
39 :
40 : /**
41 : * Converts ASCII string to floating point number.
42 : *
43 : * This function converts the initial portion of the string pointed to
44 : * by nptr to double floating point representation. The behavior is the
45 : * same as
46 : *
47 : * CPLStrtodDelim(nptr, (char **)NULL, point);
48 : *
49 : * This function does the same as standard atof(3), but does not take locale
50 : * in account. Instead of locale defined decimal delimiter you can specify
51 : * your own one. Also see notes for CPLAtof() function.
52 : *
53 : * @param nptr Pointer to string to convert.
54 : * @param point Decimal delimiter.
55 : *
56 : * @return Converted value, if any.
57 : */
58 165496 : double CPLAtofDelim(const char *nptr, char point)
59 : {
60 165496 : return CPLStrtodDelim(nptr, nullptr, point);
61 : }
62 :
63 : /************************************************************************/
64 : /* CPLAtof() */
65 : /************************************************************************/
66 :
67 : /**
68 : * Converts ASCII string to floating point number.
69 : *
70 : * This function converts the initial portion of the string pointed to
71 : * by nptr to double floating point representation. The behavior is the
72 : * same as
73 : *
74 : * CPLStrtod(nptr, (char **)NULL);
75 : *
76 : * This function does the same as standard atof(3), but does not take
77 : * locale in account. That means, the decimal delimiter is always '.'
78 : * (decimal point). Use CPLAtofDelim() function if you want to specify
79 : * custom delimiter.
80 : *
81 : * IMPORTANT NOTE:
82 : *
83 : * Existence of this function does not mean you should always use it. Sometimes
84 : * you should use standard locale aware atof(3) and its family. When you need to
85 : * process the user's input (for example, command line parameters) use atof(3),
86 : * because the user works in a localized environment and the user's input will
87 : * be done according to the locale set. In particular that means we should not
88 : * make assumptions about character used as decimal delimiter, it can be either
89 : * "." or ",".
90 : *
91 : * But when you are parsing some ASCII file in predefined format, you most
92 : * likely need CPLAtof(), because such files distributed across the systems
93 : * with different locales and floating point representation should be
94 : * considered as a part of file format. If the format uses "." as a delimiter
95 : * the same character must be used when parsing number regardless of actual
96 : * locale setting.
97 : *
98 : * @param nptr Pointer to string to convert.
99 : *
100 : * @return Converted value, if any.
101 : */
102 14717400 : double CPLAtof(const char *nptr)
103 : {
104 14717400 : return CPLStrtod(nptr, nullptr);
105 : }
106 :
107 : /************************************************************************/
108 : /* CPLAtofM() */
109 : /************************************************************************/
110 :
111 : /**
112 : * Converts ASCII string to floating point number using any numeric locale.
113 : *
114 : * This function converts the initial portion of the string pointed to
115 : * by nptr to double floating point representation. This function does the
116 : * same as standard atof(), but it allows a variety of locale representations.
117 : * That is it supports numeric values with either a comma or a period for
118 : * the decimal delimiter.
119 : *
120 : * PS. The M stands for Multi-lingual.
121 : *
122 : * @param nptr The string to convert.
123 : *
124 : * @return Converted value, if any. Zero on failure.
125 : */
126 :
127 24799 : double CPLAtofM(const char *nptr)
128 :
129 : {
130 24799 : const int nMaxSearch = 50;
131 :
132 82144 : for (int i = 0; i < nMaxSearch; i++)
133 : {
134 82144 : if (nptr[i] == ',')
135 412 : return CPLStrtodDelim(nptr, nullptr, ',');
136 81732 : if (nptr[i] == '.' || nptr[i] == '\0')
137 24387 : return CPLStrtodDelim(nptr, nullptr, '.');
138 : }
139 :
140 0 : return CPLStrtodDelim(nptr, nullptr, '.');
141 : }
142 :
143 : /************************************************************************/
144 : /* CPLReplacePointByLocalePoint() */
145 : /************************************************************************/
146 :
147 : /* Return a newly allocated variable if substitution was done, or NULL
148 : * otherwise.
149 : */
150 0 : static char *CPLReplacePointByLocalePoint(const char *pszNumber, char point)
151 : {
152 : #if defined(__ANDROID__) && __ANDROID_API__ < 20
153 : // localeconv() only available since API 20
154 : static char byPoint = 0;
155 : if (byPoint == 0)
156 : {
157 : char szBuf[16] = {};
158 : snprintf(szBuf, sizeof(szBuf), "%.1f", 1.0);
159 : byPoint = szBuf[1];
160 : }
161 : if (point != byPoint)
162 : {
163 : const char *pszPoint = strchr(pszNumber, point);
164 : if (pszPoint)
165 : {
166 : char *pszNew = CPLStrdup(pszNumber);
167 : pszNew[pszPoint - pszNumber] = byPoint;
168 : return pszNew;
169 : }
170 : }
171 : #else // ndef __ANDROID__
172 0 : struct lconv *poLconv = localeconv();
173 0 : if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
174 : {
175 0 : char byPoint = poLconv->decimal_point[0];
176 :
177 0 : if (point != byPoint)
178 : {
179 0 : const char *pszLocalePoint = strchr(pszNumber, byPoint);
180 0 : const char *pszPoint = strchr(pszNumber, point);
181 0 : if (pszPoint || pszLocalePoint)
182 : {
183 0 : char *pszNew = CPLStrdup(pszNumber);
184 0 : if (pszLocalePoint)
185 0 : pszNew[pszLocalePoint - pszNumber] = ' ';
186 0 : if (pszPoint)
187 0 : pszNew[pszPoint - pszNumber] = byPoint;
188 0 : return pszNew;
189 : }
190 : }
191 : }
192 : #endif // __ANDROID__
193 :
194 0 : return nullptr;
195 : }
196 :
197 : /************************************************************************/
198 : /* CPLStrtodDelim() */
199 : /************************************************************************/
200 :
201 : /**
202 : * Converts ASCII string to floating point number using specified delimiter.
203 : *
204 : * This function converts the initial portion of the string pointed to
205 : * by nptr to double floating point representation. This function does the
206 : * same as standard strtod(3), but does not take locale in account. Instead of
207 : * locale defined decimal delimiter you can specify your own one. Also see
208 : * notes for CPLAtof() function.
209 : *
210 : * @param nptr Pointer to string to convert.
211 : * @param endptr If is not NULL, a pointer to the character after the last
212 : * character used in the conversion is stored in the location referenced
213 : * by endptr.
214 : * @param point Decimal delimiter.
215 : *
216 : * @return Converted value, if any.
217 : */
218 15359300 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
219 : {
220 15442900 : while (*nptr == ' '
221 : #ifdef USE_FAST_FLOAT
222 : // The GSAG driver provides leading end-of-line character
223 15359300 : || *nptr == '\r' || *nptr == '\n' || *nptr == '\t'
224 : #endif
225 : )
226 : {
227 83603 : nptr++;
228 : }
229 :
230 15275700 : if (nptr[0] == '-')
231 : {
232 6542760 : if (STARTS_WITH(nptr, "-1.#QNAN") || STARTS_WITH(nptr, "-1.#IND"))
233 : {
234 2 : if (endptr)
235 2 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
236 : // While it is possible on some platforms to flip the sign
237 : // of NAN to negative, this function will always return a positive
238 : // quiet (non-signalling) NaN.
239 2 : return std::numeric_limits<double>::quiet_NaN();
240 : }
241 6542760 : if (
242 : #ifndef USE_FAST_FLOAT
243 : strcmp(nptr, "-inf") == 0 ||
244 : #endif
245 6542760 : STARTS_WITH_CI(nptr, "-1.#INF"))
246 : {
247 2 : if (endptr)
248 1 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
249 2 : return -std::numeric_limits<double>::infinity();
250 : }
251 : }
252 8732980 : else if (nptr[0] == '1')
253 : {
254 2048410 : if (STARTS_WITH(nptr, "1.#QNAN") || STARTS_WITH(nptr, "1.#SNAN"))
255 : {
256 2 : if (endptr)
257 2 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
258 2 : return std::numeric_limits<double>::quiet_NaN();
259 : }
260 2048410 : if (STARTS_WITH_CI(nptr, "1.#INF"))
261 : {
262 2 : if (endptr)
263 1 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
264 2 : return std::numeric_limits<double>::infinity();
265 : }
266 : }
267 : #ifndef USE_FAST_FLOAT
268 : else if (nptr[0] == 'i' && strcmp(nptr, "inf") == 0)
269 : {
270 : if (endptr)
271 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
272 : return std::numeric_limits<double>::infinity();
273 : }
274 : else if (nptr[0] == 'n' && strcmp(nptr, "nan") == 0)
275 : {
276 : if (endptr)
277 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
278 : return std::numeric_limits<double>::quiet_NaN();
279 : }
280 : #endif
281 :
282 : #ifdef USE_FAST_FLOAT
283 : // Skip leading '+' as non-handled by fast_float
284 15275700 : if (*nptr == '+')
285 4124 : nptr++;
286 :
287 : // Find a reasonable position for the end of the string to provide to
288 : // fast_float
289 15275700 : const char *endptrIn = nptr;
290 126892000 : while ((*endptrIn >= '0' && *endptrIn <= '9') || *endptrIn == point ||
291 148927000 : *endptrIn == '+' || *endptrIn == '-' || *endptrIn == 'e' ||
292 15371700 : *endptrIn == 'E')
293 : {
294 111616000 : ++endptrIn;
295 : }
296 :
297 15275700 : double dfValue = 0;
298 : const fast_float::parse_options options{fast_float::chars_format::general,
299 15275700 : point};
300 : auto answer =
301 15275700 : fast_float::from_chars_advanced(nptr, endptrIn, dfValue, options);
302 15275700 : if (answer.ec != std::errc())
303 : {
304 41775 : if (strcmp(nptr, "-inf") == 0)
305 : {
306 63 : dfValue = -std::numeric_limits<double>::infinity();
307 63 : answer.ptr = nptr + strlen("-inf");
308 : }
309 41712 : else if ( // Generated by SQLite (impacts ogr_gpkg tests)
310 41712 : strcmp(nptr, "-Inf") == 0)
311 : {
312 4 : dfValue = -std::numeric_limits<double>::infinity();
313 4 : answer.ptr = nptr + strlen("-Inf");
314 : }
315 41708 : else if (
316 : // Reported by user as being understood in previous GDAL versions
317 41708 : strcmp(nptr, "-INF") == 0)
318 : {
319 1 : dfValue = -std::numeric_limits<double>::infinity();
320 1 : answer.ptr = nptr + strlen("-INF");
321 : }
322 41707 : else if (
323 : // Triggered by ogr_pg tests
324 41707 : strcmp(nptr, "-Infinity") == 0)
325 : {
326 17 : dfValue = -std::numeric_limits<double>::infinity();
327 17 : answer.ptr = nptr + strlen("-Infinity");
328 : }
329 41690 : else if (strcmp(nptr, "inf") == 0)
330 : {
331 107 : dfValue = std::numeric_limits<double>::infinity();
332 107 : answer.ptr = nptr + strlen("inf");
333 : }
334 41583 : else if ( // Generated by SQLite (impacts ogr_gpkg tests)
335 41583 : strcmp(nptr, "Inf") == 0)
336 : {
337 4 : dfValue = std::numeric_limits<double>::infinity();
338 4 : answer.ptr = nptr + strlen("Inf");
339 : }
340 41579 : else if (
341 : // Reported by user as being understood in previous GDAL versions
342 41579 : strcmp(nptr, "INF") == 0)
343 : {
344 1 : dfValue = std::numeric_limits<double>::infinity();
345 1 : answer.ptr = nptr + strlen("INF");
346 : }
347 41578 : else if (
348 : // Triggered by ogr_pg tests
349 41578 : strcmp(nptr, "Infinity") == 0)
350 : {
351 13 : dfValue = std::numeric_limits<double>::infinity();
352 13 : answer.ptr = nptr + strlen("Infinity");
353 : }
354 41565 : else if (strcmp(nptr, "nan") == 0)
355 : {
356 116 : dfValue = std::numeric_limits<double>::quiet_NaN();
357 116 : answer.ptr = nptr + strlen("nan");
358 : }
359 41449 : else if (
360 : // Triggered by ogr_pg tests
361 41449 : strcmp(nptr, "NaN") == 0)
362 : {
363 15 : dfValue = std::numeric_limits<double>::quiet_NaN();
364 15 : answer.ptr = nptr + strlen("NaN");
365 : }
366 : else
367 : {
368 41434 : errno = answer.ptr == nptr ? 0 : ERANGE;
369 : }
370 : }
371 15275700 : if (endptr)
372 : {
373 364476 : *endptr = const_cast<char *>(answer.ptr);
374 : }
375 : #else
376 : /* -------------------------------------------------------------------- */
377 : /* We are implementing a simple method here: copy the input string */
378 : /* into the temporary buffer, replace the specified decimal delimiter */
379 : /* with the one, taken from locale settings and use standard strtod() */
380 : /* on that buffer. */
381 : /* -------------------------------------------------------------------- */
382 : char *pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
383 : const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
384 :
385 : const double dfValue = strtod(pszNumber, endptr);
386 : const int nError = errno;
387 :
388 : if (endptr)
389 : *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
390 :
391 : if (pszNewNumberOrNull)
392 : CPLFree(pszNewNumberOrNull);
393 :
394 : errno = nError;
395 : #endif
396 :
397 15275700 : return dfValue;
398 : }
399 :
400 : /************************************************************************/
401 : /* CPLStrtod() */
402 : /************************************************************************/
403 :
404 : /**
405 : * Converts ASCII string to floating point number.
406 : *
407 : * This function converts the initial portion of the string pointed to
408 : * by nptr to double floating point representation. This function does the
409 : * same as standard strtod(3), but does not take locale in account. That
410 : * means, the decimal delimiter is always '.' (decimal point). Use
411 : * CPLStrtodDelim() function if you want to specify custom delimiter. Also
412 : * see notes for CPLAtof() function.
413 : *
414 : * @param nptr Pointer to string to convert.
415 : * @param endptr If is not NULL, a pointer to the character after the last
416 : * character used in the conversion is stored in the location referenced
417 : * by endptr.
418 : *
419 : * @return Converted value, if any.
420 : */
421 15076100 : double CPLStrtod(const char *nptr, char **endptr)
422 : {
423 15076100 : return CPLStrtodDelim(nptr, endptr, '.');
424 : }
425 :
426 : /************************************************************************/
427 : /* CPLStrtodM() */
428 : /************************************************************************/
429 :
430 : /**
431 : * Converts ASCII string to floating point number.
432 : *
433 : * This function converts the initial portion of the string pointed to
434 : * by nptr to double floating point representation. This function does the
435 : * same as standard strtod(3), but does not take locale in account.
436 : *
437 : * That function accepts '.' (decimal point) or ',' (comma) as decimal
438 : * delimiter.
439 : *
440 : * @param nptr Pointer to string to convert.
441 : * @param endptr If is not NULL, a pointer to the character after the last
442 : * character used in the conversion is stored in the location referenced
443 : * by endptr.
444 : *
445 : * @return Converted value, if any.
446 : * @since GDAL 3.9
447 : */
448 6249 : double CPLStrtodM(const char *nptr, char **endptr)
449 :
450 : {
451 6249 : const int nMaxSearch = 50;
452 :
453 26535 : for (int i = 0; i < nMaxSearch; i++)
454 : {
455 26535 : if (nptr[i] == ',')
456 0 : return CPLStrtodDelim(nptr, endptr, ',');
457 26535 : if (nptr[i] == '.' || nptr[i] == '\0')
458 6249 : return CPLStrtodDelim(nptr, endptr, '.');
459 : }
460 :
461 0 : return CPLStrtodDelim(nptr, endptr, '.');
462 : }
463 :
464 : /************************************************************************/
465 : /* CPLStrtofDelim() */
466 : /************************************************************************/
467 :
468 : /**
469 : * Converts ASCII string to floating point number using specified delimiter.
470 : *
471 : * This function converts the initial portion of the string pointed to
472 : * by nptr to single floating point representation. This function does the
473 : * same as standard strtof(3), but does not take locale in account. Instead of
474 : * locale defined decimal delimiter you can specify your own one. Also see
475 : * notes for CPLAtof() function.
476 : *
477 : * @param nptr Pointer to string to convert.
478 : * @param endptr If is not NULL, a pointer to the character after the last
479 : * character used in the conversion is stored in the location referenced
480 : * by endptr.
481 : * @param point Decimal delimiter.
482 : *
483 : * @return Converted value, if any.
484 : */
485 0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
486 : {
487 : /* -------------------------------------------------------------------- */
488 : /* We are implementing a simple method here: copy the input string */
489 : /* into the temporary buffer, replace the specified decimal delimiter */
490 : /* with the one, taken from locale settings and use standard strtof() */
491 : /* on that buffer. */
492 : /* -------------------------------------------------------------------- */
493 0 : char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
494 0 : const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
495 0 : const float fValue = strtof(pszNumber, endptr);
496 0 : const int nError = errno;
497 :
498 0 : if (endptr)
499 0 : *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
500 :
501 0 : if (pszNewNumberOrNull)
502 0 : CPLFree(pszNewNumberOrNull);
503 :
504 0 : errno = nError;
505 0 : return fValue;
506 : }
507 :
508 : /************************************************************************/
509 : /* CPLStrtof() */
510 : /************************************************************************/
511 :
512 : /**
513 : * Converts ASCII string to floating point number.
514 : *
515 : * This function converts the initial portion of the string pointed to
516 : * by nptr to single floating point representation. This function does the
517 : * same as standard strtof(3), but does not take locale in account. That
518 : * means, the decimal delimiter is always '.' (decimal point). Use
519 : * CPLStrtofDelim() function if you want to specify custom delimiter. Also
520 : * see notes for CPLAtof() function.
521 : *
522 : * @param nptr Pointer to string to convert.
523 : * @param endptr If is not NULL, a pointer to the character after the last
524 : * character used in the conversion is stored in the location referenced
525 : * by endptr.
526 : *
527 : * @return Converted value, if any.
528 : */
529 0 : float CPLStrtof(const char *nptr, char **endptr)
530 : {
531 0 : return CPLStrtofDelim(nptr, endptr, '.');
532 : }
|