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