Coverage Report

Created: 2026-07-25 06:50

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
0
{
62
0
    return CPLStrtodDelim(nptr, nullptr, point);
63
0
}
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
0
{
106
0
    return CPLStrtod(nptr, nullptr);
107
0
}
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
0
{
132
0
    const int nMaxSearch = 50;
133
134
0
    for (int i = 0; i < nMaxSearch; i++)
135
0
    {
136
0
        if (nptr[i] == ',')
137
0
            return CPLStrtodDelim(nptr, nullptr, ',');
138
0
        if (nptr[i] == '.' || nptr[i] == '\0')
139
0
            return CPLStrtodDelim(nptr, nullptr, '.');
140
0
    }
141
142
0
    return CPLStrtodDelim(nptr, nullptr, '.');
143
0
}
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
0
{
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
0
    struct lconv *poLconv = localeconv();
175
0
    if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
176
0
    {
177
0
        char byPoint = poLconv->decimal_point[0];
178
179
0
        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
0
    }
194
0
#endif  // __ANDROID__
195
196
0
    return nullptr;
197
0
}
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
0
{
222
0
    return cpl::strtod_delim(nptr, endptr, point);
223
0
}
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
0
{
246
0
    str = trim(str);
247
248
0
    if (str.empty())
249
0
    {
250
0
        if (endptr)
251
0
            *endptr = const_cast<char *>(str.data());
252
0
        return 0;
253
0
    }
254
255
0
    if (str[0] == '-')
256
0
    {
257
0
        if (starts_with(str, "-1.#QNAN") || starts_with(str, "-1.#IND"))
258
0
        {
259
0
            if (endptr)
260
0
                *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
0
            return std::numeric_limits<double>::quiet_NaN();
265
0
        }
266
0
        if (starts_with_ci(str, "-1.#INF"))
267
0
        {
268
0
            if (endptr)
269
0
                *endptr = const_cast<char *>(str.data()) + str.size();
270
0
            return -std::numeric_limits<double>::infinity();
271
0
        }
272
0
    }
273
0
    else if (str[0] == '1')
274
0
    {
275
0
        if (starts_with(str, "1.#QNAN") || starts_with(str, "1.#SNAN"))
276
0
        {
277
0
            if (endptr)
278
0
                *endptr = const_cast<char *>(str.data()) + str.size();
279
0
            return std::numeric_limits<double>::quiet_NaN();
280
0
        }
281
0
        if (starts_with_ci(str, "1.#INF"))
282
0
        {
283
0
            if (endptr)
284
0
                *endptr = const_cast<char *>(str.data()) + str.size();
285
0
            return std::numeric_limits<double>::infinity();
286
0
        }
287
0
    }
288
289
    // Skip leading '+' as non-handled by fast_float
290
0
    if (str[0] == '+')
291
0
        str = str.substr(1);
292
293
    // Find a reasonable position for the end of the string to provide to
294
    // fast_float
295
0
    std::ptrdiff_t endPos = 0;
296
0
    while (endPos < static_cast<std::ptrdiff_t>(str.size()) &&
297
0
           ((str[endPos] >= '0' && str[endPos] <= '9') ||
298
0
            str[endPos] == point || str[endPos] == '+' || str[endPos] == '-' ||
299
0
            str[endPos] == 'e' || str[endPos] == 'E'))
300
0
    {
301
0
        ++endPos;
302
0
    }
303
304
0
    double dfValue = 0;
305
0
    const fast_float::parse_options options{fast_float::chars_format::general,
306
0
                                            point};
307
0
    auto answer = fast_float::from_chars_advanced(
308
0
        str.data(), str.data() + endPos, dfValue, options);
309
0
    if (answer.ec != std::errc())
310
0
    {
311
0
        if (
312
            // Triggered by ogr_pg tests
313
0
            starts_with_ci(str, "-Infinity"))
314
0
        {
315
0
            dfValue = -std::numeric_limits<double>::infinity();
316
0
            answer.ptr = str.data() + strlen("-Infinity");
317
0
        }
318
0
        else if (starts_with_ci(str, "-inf"))
319
0
        {
320
0
            dfValue = -std::numeric_limits<double>::infinity();
321
0
            answer.ptr = str.data() + strlen("-inf");
322
0
        }
323
0
        else if (
324
            // Triggered by ogr_pg tests
325
0
            starts_with_ci(str, "Infinity"))
326
0
        {
327
0
            dfValue = std::numeric_limits<double>::infinity();
328
0
            answer.ptr = str.data() + strlen("Infinity");
329
0
        }
330
0
        else if (cpl::starts_with_ci(str, "inf"))
331
0
        {
332
0
            dfValue = std::numeric_limits<double>::infinity();
333
0
            answer.ptr = str.data() + strlen("inf");
334
0
        }
335
0
        else if (cpl::starts_with_ci(str, "nan"))
336
0
        {
337
0
            dfValue = std::numeric_limits<double>::quiet_NaN();
338
0
            answer.ptr = str.data() + strlen("nan");
339
0
        }
340
0
        else
341
0
        {
342
0
            errno = answer.ptr == str.data() ? 0 : ERANGE;
343
0
        }
344
0
    }
345
0
    if (endptr)
346
0
    {
347
0
        *endptr = const_cast<char *>(answer.ptr);
348
0
    }
349
350
0
    return dfValue;
351
0
}
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
0
{
377
0
    return CPLStrtodDelim(nptr, endptr, '.');
378
0
}
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
0
{
405
0
    const int nMaxSearch = 50;
406
407
0
    for (int i = 0; i < nMaxSearch; i++)
408
0
    {
409
0
        if (nptr[i] == ',')
410
0
            return CPLStrtodDelim(nptr, endptr, ',');
411
0
        if (nptr[i] == '.' || nptr[i] == '\0')
412
0
            return CPLStrtodDelim(nptr, endptr, '.');
413
0
    }
414
415
0
    return CPLStrtodDelim(nptr, endptr, '.');
416
0
}
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
0
{
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
0
    char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
448
0
    const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
449
0
    const float fValue = strtof(pszNumber, endptr);
450
0
    const int nError = errno;
451
452
0
    if (endptr)
453
0
        *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
454
455
0
    if (pszNewNumberOrNull)
456
0
        CPLFree(pszNewNumberOrNull);
457
458
0
    errno = nError;
459
0
    return fValue;
460
0
}
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
0
{
485
0
    return CPLStrtofDelim(nptr, endptr, '.');
486
0
}