/src/gdal/port/cpl_strtod.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | double CPLAtofDelim(const char *nptr, char point) |
59 | 0 | { |
60 | 0 | return CPLStrtodDelim(nptr, nullptr, point); |
61 | 0 | } |
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 | | double CPLAtof(const char *nptr) |
103 | 0 | { |
104 | 0 | return CPLStrtod(nptr, nullptr); |
105 | 0 | } |
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 | | double CPLAtofM(const char *nptr) |
128 | | |
129 | 0 | { |
130 | 0 | const int nMaxSearch = 50; |
131 | |
|
132 | 0 | for (int i = 0; i < nMaxSearch; i++) |
133 | 0 | { |
134 | 0 | if (nptr[i] == ',') |
135 | 0 | return CPLStrtodDelim(nptr, nullptr, ','); |
136 | 0 | if (nptr[i] == '.' || nptr[i] == '\0') |
137 | 0 | return CPLStrtodDelim(nptr, nullptr, '.'); |
138 | 0 | } |
139 | | |
140 | 0 | return CPLStrtodDelim(nptr, nullptr, '.'); |
141 | 0 | } |
142 | | |
143 | | /************************************************************************/ |
144 | | /* CPLReplacePointByLocalePoint() */ |
145 | | /************************************************************************/ |
146 | | |
147 | | /* Return a newly allocated variable if substitution was done, or NULL |
148 | | * otherwise. |
149 | | */ |
150 | | static char *CPLReplacePointByLocalePoint(const char *pszNumber, char point) |
151 | 0 | { |
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 | 0 | { |
175 | 0 | char byPoint = poLconv->decimal_point[0]; |
176 | |
|
177 | 0 | if (point != byPoint) |
178 | 0 | { |
179 | 0 | const char *pszLocalePoint = strchr(pszNumber, byPoint); |
180 | 0 | const char *pszPoint = strchr(pszNumber, point); |
181 | 0 | if (pszPoint || pszLocalePoint) |
182 | 0 | { |
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 | 0 | } |
190 | 0 | } |
191 | 0 | } |
192 | 0 | #endif // __ANDROID__ |
193 | | |
194 | 0 | return nullptr; |
195 | 0 | } |
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 | | double CPLStrtodDelim(const char *nptr, char **endptr, char point) |
219 | 0 | { |
220 | 0 | while (*nptr == ' ' |
221 | 0 | #ifdef USE_FAST_FLOAT |
222 | | // The GSAG driver provides leading end-of-line character |
223 | 0 | || *nptr == '\r' || *nptr == '\n' || *nptr == '\t' |
224 | 0 | #endif |
225 | 0 | ) |
226 | 0 | { |
227 | 0 | nptr++; |
228 | 0 | } |
229 | |
|
230 | 0 | if (nptr[0] == '-') |
231 | 0 | { |
232 | 0 | if (STARTS_WITH(nptr, "-1.#QNAN") || STARTS_WITH(nptr, "-1.#IND")) |
233 | 0 | { |
234 | 0 | if (endptr) |
235 | 0 | *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 | 0 | return std::numeric_limits<double>::quiet_NaN(); |
240 | 0 | } |
241 | 0 | if ( |
242 | | #ifndef USE_FAST_FLOAT |
243 | | strcmp(nptr, "-inf") == 0 || |
244 | | #endif |
245 | 0 | STARTS_WITH_CI(nptr, "-1.#INF")) |
246 | 0 | { |
247 | 0 | if (endptr) |
248 | 0 | *endptr = const_cast<char *>(nptr) + strlen(nptr); |
249 | 0 | return -std::numeric_limits<double>::infinity(); |
250 | 0 | } |
251 | 0 | } |
252 | 0 | else if (nptr[0] == '1') |
253 | 0 | { |
254 | 0 | if (STARTS_WITH(nptr, "1.#QNAN") || STARTS_WITH(nptr, "1.#SNAN")) |
255 | 0 | { |
256 | 0 | if (endptr) |
257 | 0 | *endptr = const_cast<char *>(nptr) + strlen(nptr); |
258 | 0 | return std::numeric_limits<double>::quiet_NaN(); |
259 | 0 | } |
260 | 0 | if (STARTS_WITH_CI(nptr, "1.#INF")) |
261 | 0 | { |
262 | 0 | if (endptr) |
263 | 0 | *endptr = const_cast<char *>(nptr) + strlen(nptr); |
264 | 0 | return std::numeric_limits<double>::infinity(); |
265 | 0 | } |
266 | 0 | } |
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 | 0 | #ifdef USE_FAST_FLOAT |
283 | | // Skip leading '+' as non-handled by fast_float |
284 | 0 | if (*nptr == '+') |
285 | 0 | nptr++; |
286 | | |
287 | | // Find a reasonable position for the end of the string to provide to |
288 | | // fast_float |
289 | 0 | const char *endptrIn = nptr; |
290 | 0 | while ((*endptrIn >= '0' && *endptrIn <= '9') || *endptrIn == point || |
291 | 0 | *endptrIn == '+' || *endptrIn == '-' || *endptrIn == 'e' || |
292 | 0 | *endptrIn == 'E') |
293 | 0 | { |
294 | 0 | ++endptrIn; |
295 | 0 | } |
296 | |
|
297 | 0 | double dfValue = 0; |
298 | 0 | const fast_float::parse_options options{fast_float::chars_format::general, |
299 | 0 | point}; |
300 | 0 | auto answer = |
301 | 0 | fast_float::from_chars_advanced(nptr, endptrIn, dfValue, options); |
302 | 0 | if (answer.ec != std::errc()) |
303 | 0 | { |
304 | 0 | if ( |
305 | | // Triggered by ogr_pg tests |
306 | 0 | STARTS_WITH_CI(nptr, "-Infinity")) |
307 | 0 | { |
308 | 0 | dfValue = -std::numeric_limits<double>::infinity(); |
309 | 0 | answer.ptr = nptr + strlen("-Infinity"); |
310 | 0 | } |
311 | 0 | else if (STARTS_WITH_CI(nptr, "-inf")) |
312 | 0 | { |
313 | 0 | dfValue = -std::numeric_limits<double>::infinity(); |
314 | 0 | answer.ptr = nptr + strlen("-inf"); |
315 | 0 | } |
316 | 0 | else if ( |
317 | | // Triggered by ogr_pg tests |
318 | 0 | STARTS_WITH_CI(nptr, "Infinity")) |
319 | 0 | { |
320 | 0 | dfValue = std::numeric_limits<double>::infinity(); |
321 | 0 | answer.ptr = nptr + strlen("Infinity"); |
322 | 0 | } |
323 | 0 | else if (STARTS_WITH_CI(nptr, "inf")) |
324 | 0 | { |
325 | 0 | dfValue = std::numeric_limits<double>::infinity(); |
326 | 0 | answer.ptr = nptr + strlen("inf"); |
327 | 0 | } |
328 | 0 | else if (STARTS_WITH_CI(nptr, "nan")) |
329 | 0 | { |
330 | 0 | dfValue = std::numeric_limits<double>::quiet_NaN(); |
331 | 0 | answer.ptr = nptr + strlen("nan"); |
332 | 0 | } |
333 | 0 | else |
334 | 0 | { |
335 | 0 | errno = answer.ptr == nptr ? 0 : ERANGE; |
336 | 0 | } |
337 | 0 | } |
338 | 0 | if (endptr) |
339 | 0 | { |
340 | 0 | *endptr = const_cast<char *>(answer.ptr); |
341 | 0 | } |
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 | 0 | return dfValue; |
365 | 0 | } |
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 | | double CPLStrtod(const char *nptr, char **endptr) |
389 | 0 | { |
390 | 0 | return CPLStrtodDelim(nptr, endptr, '.'); |
391 | 0 | } |
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 | | double CPLStrtodM(const char *nptr, char **endptr) |
416 | | |
417 | 0 | { |
418 | 0 | const int nMaxSearch = 50; |
419 | |
|
420 | 0 | for (int i = 0; i < nMaxSearch; i++) |
421 | 0 | { |
422 | 0 | if (nptr[i] == ',') |
423 | 0 | return CPLStrtodDelim(nptr, endptr, ','); |
424 | 0 | if (nptr[i] == '.' || nptr[i] == '\0') |
425 | 0 | return CPLStrtodDelim(nptr, endptr, '.'); |
426 | 0 | } |
427 | | |
428 | 0 | return CPLStrtodDelim(nptr, endptr, '.'); |
429 | 0 | } |
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 | | float CPLStrtofDelim(const char *nptr, char **endptr, char point) |
453 | 0 | { |
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 | 0 | char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point); |
461 | 0 | const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr; |
462 | 0 | const float fValue = strtof(pszNumber, endptr); |
463 | 0 | const int nError = errno; |
464 | |
|
465 | 0 | if (endptr) |
466 | 0 | *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber); |
467 | |
|
468 | 0 | if (pszNewNumberOrNull) |
469 | 0 | CPLFree(pszNewNumberOrNull); |
470 | |
|
471 | 0 | errno = nError; |
472 | 0 | return fValue; |
473 | 0 | } |
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 | | float CPLStrtof(const char *nptr, char **endptr) |
497 | 0 | { |
498 | 0 | return CPLStrtofDelim(nptr, endptr, '.'); |
499 | 0 | } |