Coverage Report

Created: 2026-08-14 09:29

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