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 167096 : double CPLAtofDelim(const char *nptr, char point)
59 : {
60 167096 : 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 14737900 : double CPLAtof(const char *nptr)
103 : {
104 14737900 : 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 25918 : double CPLAtofM(const char *nptr)
128 :
129 : {
130 25918 : const int nMaxSearch = 50;
131 :
132 85507 : for (int i = 0; i < nMaxSearch; i++)
133 : {
134 85507 : if (nptr[i] == ',')
135 412 : return CPLStrtodDelim(nptr, nullptr, ',');
136 85095 : if (nptr[i] == '.' || nptr[i] == '\0')
137 25506 : 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 10 : 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 10 : struct lconv *poLconv = localeconv();
173 10 : if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
174 : {
175 10 : char byPoint = poLconv->decimal_point[0];
176 :
177 10 : 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 10 : 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 15451200 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
219 : {
220 15528500 : while (*nptr == ' '
221 : #ifdef USE_FAST_FLOAT
222 : // The GSAG driver provides leading end-of-line character
223 15451200 : || *nptr == '\r' || *nptr == '\n' || *nptr == '\t'
224 : #endif
225 : )
226 : {
227 77248 : nptr++;
228 : }
229 :
230 15374000 : if (nptr[0] == '-')
231 : {
232 6565920 : 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 6565910 : if (
242 : #ifndef USE_FAST_FLOAT
243 : strcmp(nptr, "-inf") == 0 ||
244 : #endif
245 6565910 : STARTS_WITH_CI(nptr, "-1.#INF"))
246 : {
247 3 : if (endptr)
248 1 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
249 3 : return -std::numeric_limits<double>::infinity();
250 : }
251 : }
252 8808070 : else if (nptr[0] == '1')
253 : {
254 2064780 : if (STARTS_WITH(nptr, "1.#QNAN") || STARTS_WITH(nptr, "1.#SNAN"))
255 : {
256 0 : if (endptr)
257 2 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
258 0 : return std::numeric_limits<double>::quiet_NaN();
259 : }
260 2064780 : if (STARTS_WITH_CI(nptr, "1.#INF"))
261 : {
262 3 : if (endptr)
263 1 : *endptr = const_cast<char *>(nptr) + strlen(nptr);
264 3 : 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 15374000 : if (*nptr == '+')
285 4179 : nptr++;
286 :
287 : // Find a reasonable position for the end of the string to provide to
288 : // fast_float
289 15374000 : const char *endptrIn = nptr;
290 128091000 : while ((*endptrIn >= '0' && *endptrIn <= '9') || *endptrIn == point ||
291 150244000 : *endptrIn == '+' || *endptrIn == '-' || *endptrIn == 'e' ||
292 15468300 : *endptrIn == 'E')
293 : {
294 112717000 : ++endptrIn;
295 : }
296 :
297 15374000 : double dfValue = 0;
298 : const fast_float::parse_options options{fast_float::chars_format::general,
299 15374000 : point};
300 : auto answer =
301 15374000 : fast_float::from_chars_advanced(nptr, endptrIn, dfValue, options);
302 15374000 : if (answer.ec != std::errc())
303 : {
304 43176 : if (
305 : // Triggered by ogr_pg tests
306 43176 : STARTS_WITH_CI(nptr, "-Infinity"))
307 : {
308 17 : dfValue = -std::numeric_limits<double>::infinity();
309 17 : answer.ptr = nptr + strlen("-Infinity");
310 : }
311 43159 : else if (STARTS_WITH_CI(nptr, "-inf"))
312 : {
313 68 : dfValue = -std::numeric_limits<double>::infinity();
314 68 : answer.ptr = nptr + strlen("-inf");
315 : }
316 43091 : else if (
317 : // Triggered by ogr_pg tests
318 43091 : STARTS_WITH_CI(nptr, "Infinity"))
319 : {
320 13 : dfValue = std::numeric_limits<double>::infinity();
321 13 : answer.ptr = nptr + strlen("Infinity");
322 : }
323 43078 : else if (STARTS_WITH_CI(nptr, "inf"))
324 : {
325 128 : dfValue = std::numeric_limits<double>::infinity();
326 128 : answer.ptr = nptr + strlen("inf");
327 : }
328 42950 : else if (STARTS_WITH_CI(nptr, "nan"))
329 : {
330 165 : dfValue = std::numeric_limits<double>::quiet_NaN();
331 165 : answer.ptr = nptr + strlen("nan");
332 : }
333 : else
334 : {
335 42785 : errno = answer.ptr == nptr ? 0 : ERANGE;
336 : }
337 : }
338 15374000 : if (endptr)
339 : {
340 439484 : *endptr = const_cast<char *>(answer.ptr);
341 : }
342 : #else
343 : /* -------------------------------------------------------------------- */
344 : /* We are implementing a simple method here: copy the input string */
345 : /* into the temporary buffer, replace the specified decimal delimiter */
346 : /* with the one, taken from locale settings and use standard strtod() */
347 : /* on that buffer. */
348 : /* -------------------------------------------------------------------- */
349 : char *pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
350 : const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
351 :
352 : const double dfValue = strtod(pszNumber, endptr);
353 : const int nError = errno;
354 :
355 : if (endptr)
356 : *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
357 :
358 : if (pszNewNumberOrNull)
359 : CPLFree(pszNewNumberOrNull);
360 :
361 : errno = nError;
362 : #endif
363 :
364 15374000 : return dfValue;
365 : }
366 :
367 : /************************************************************************/
368 : /* CPLStrtod() */
369 : /************************************************************************/
370 :
371 : /**
372 : * Converts ASCII string to floating point number.
373 : *
374 : * This function converts the initial portion of the string pointed to
375 : * by nptr to double floating point representation. This function does the
376 : * same as standard strtod(3), but does not take locale in account. That
377 : * means, the decimal delimiter is always '.' (decimal point). Use
378 : * CPLStrtodDelim() function if you want to specify custom delimiter. Also
379 : * see notes for CPLAtof() function.
380 : *
381 : * @param nptr Pointer to string to convert.
382 : * @param endptr If is not NULL, a pointer to the character after the last
383 : * character used in the conversion is stored in the location referenced
384 : * by endptr.
385 : *
386 : * @return Converted value, if any.
387 : */
388 15165300 : double CPLStrtod(const char *nptr, char **endptr)
389 : {
390 15165300 : return CPLStrtodDelim(nptr, endptr, '.');
391 : }
392 :
393 : /************************************************************************/
394 : /* CPLStrtodM() */
395 : /************************************************************************/
396 :
397 : /**
398 : * Converts ASCII string to floating point number.
399 : *
400 : * This function converts the initial portion of the string pointed to
401 : * by nptr to double floating point representation. This function does the
402 : * same as standard strtod(3), but does not take locale in account.
403 : *
404 : * That function accepts '.' (decimal point) or ',' (comma) as decimal
405 : * delimiter.
406 : *
407 : * @param nptr Pointer to string to convert.
408 : * @param endptr If is not NULL, a pointer to the character after the last
409 : * character used in the conversion is stored in the location referenced
410 : * by endptr.
411 : *
412 : * @return Converted value, if any.
413 : * @since GDAL 3.9
414 : */
415 12808 : double CPLStrtodM(const char *nptr, char **endptr)
416 :
417 : {
418 12808 : const int nMaxSearch = 50;
419 :
420 64132 : for (int i = 0; i < nMaxSearch; i++)
421 : {
422 64132 : if (nptr[i] == ',')
423 0 : return CPLStrtodDelim(nptr, endptr, ',');
424 64132 : if (nptr[i] == '.' || nptr[i] == '\0')
425 12808 : return CPLStrtodDelim(nptr, endptr, '.');
426 : }
427 :
428 0 : return CPLStrtodDelim(nptr, endptr, '.');
429 : }
430 :
431 : /************************************************************************/
432 : /* CPLStrtofDelim() */
433 : /************************************************************************/
434 :
435 : /**
436 : * Converts ASCII string to floating point number using specified delimiter.
437 : *
438 : * This function converts the initial portion of the string pointed to
439 : * by nptr to single floating point representation. This function does the
440 : * same as standard strtof(3), but does not take locale in account. Instead of
441 : * locale defined decimal delimiter you can specify your own one. Also see
442 : * notes for CPLAtof() function.
443 : *
444 : * @param nptr Pointer to string to convert.
445 : * @param endptr If is not NULL, a pointer to the character after the last
446 : * character used in the conversion is stored in the location referenced
447 : * by endptr.
448 : * @param point Decimal delimiter.
449 : *
450 : * @return Converted value, if any.
451 : */
452 10 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
453 : {
454 : /* -------------------------------------------------------------------- */
455 : /* We are implementing a simple method here: copy the input string */
456 : /* into the temporary buffer, replace the specified decimal delimiter */
457 : /* with the one, taken from locale settings and use standard strtof() */
458 : /* on that buffer. */
459 : /* -------------------------------------------------------------------- */
460 10 : char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
461 10 : const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
462 10 : const float fValue = strtof(pszNumber, endptr);
463 10 : const int nError = errno;
464 :
465 10 : if (endptr)
466 10 : *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
467 :
468 10 : if (pszNewNumberOrNull)
469 0 : CPLFree(pszNewNumberOrNull);
470 :
471 10 : errno = nError;
472 10 : return fValue;
473 : }
474 :
475 : /************************************************************************/
476 : /* CPLStrtof() */
477 : /************************************************************************/
478 :
479 : /**
480 : * Converts ASCII string to floating point number.
481 : *
482 : * This function converts the initial portion of the string pointed to
483 : * by nptr to single floating point representation. This function does the
484 : * same as standard strtof(3), but does not take locale in account. That
485 : * means, the decimal delimiter is always '.' (decimal point). Use
486 : * CPLStrtofDelim() function if you want to specify custom delimiter. Also
487 : * see notes for CPLAtof() function.
488 : *
489 : * @param nptr Pointer to string to convert.
490 : * @param endptr If is not NULL, a pointer to the character after the last
491 : * character used in the conversion is stored in the location referenced
492 : * by endptr.
493 : *
494 : * @return Converted value, if any.
495 : */
496 10 : float CPLStrtof(const char *nptr, char **endptr)
497 : {
498 10 : return CPLStrtofDelim(nptr, endptr, '.');
499 : }
|