LCOV - code coverage report
Current view: top level - port - cpl_strtod.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 96 109 88.1 %
Date: 2026-07-09 08:35:43 Functions: 10 10 100.0 %

          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             : #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      169899 : double CPLAtofDelim(const char *nptr, char point)
      61             : {
      62      169899 :     return CPLStrtodDelim(nptr, nullptr, point);
      63             : }
      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     1034800 : double CPLAtof(const char *nptr)
     105             : {
     106     1034800 :     return CPLStrtod(nptr, nullptr);
     107             : }
     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       26634 : double CPLAtofM(const char *nptr)
     130             : 
     131             : {
     132       26634 :     const int nMaxSearch = 50;
     133             : 
     134       87357 :     for (int i = 0; i < nMaxSearch; i++)
     135             :     {
     136       87357 :         if (nptr[i] == ',')
     137         412 :             return CPLStrtodDelim(nptr, nullptr, ',');
     138       86945 :         if (nptr[i] == '.' || nptr[i] == '\0')
     139       26222 :             return CPLStrtodDelim(nptr, nullptr, '.');
     140             :     }
     141             : 
     142           0 :     return CPLStrtodDelim(nptr, nullptr, '.');
     143             : }
     144             : 
     145             : /************************************************************************/
     146             : /*                    CPLReplacePointByLocalePoint()                    */
     147             : /************************************************************************/
     148             : 
     149             : /* Return a newly allocated variable if substitution was done, or NULL
     150             :  * otherwise.
     151             :  */
     152          10 : static char *CPLReplacePointByLocalePoint(const char *pszNumber, char point)
     153             : {
     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          10 :     struct lconv *poLconv = localeconv();
     175          10 :     if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
     176             :     {
     177          10 :         char byPoint = poLconv->decimal_point[0];
     178             : 
     179          10 :         if (point != byPoint)
     180             :         {
     181           0 :             const char *pszLocalePoint = strchr(pszNumber, byPoint);
     182           0 :             const char *pszPoint = strchr(pszNumber, point);
     183           0 :             if (pszPoint || pszLocalePoint)
     184             :             {
     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             :             }
     192             :         }
     193             :     }
     194             : #endif  // __ANDROID__
     195             : 
     196          10 :     return nullptr;
     197             : }
     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     1732130 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
     221             : {
     222     1732130 :     return cpl::strtod_delim(nptr, endptr, point);
     223             : }
     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     1732130 : double strtod_delim(std::string_view str, char **endptr, char point)
     245             : {
     246     1732130 :     str = trim(str);
     247             : 
     248     1732130 :     if (str.empty())
     249             :     {
     250       14410 :         if (endptr)
     251          61 :             *endptr = const_cast<char *>(str.data());
     252       14410 :         return 0;
     253             :     }
     254             : 
     255     1717720 :     if (str[0] == '-')
     256             :     {
     257      174003 :         if (starts_with(str, "-1.#QNAN") || starts_with(str, "-1.#IND"))
     258             :         {
     259           2 :             if (endptr)
     260           2 :                 *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           2 :             return std::numeric_limits<double>::quiet_NaN();
     265             :         }
     266      174001 :         if (starts_with_ci(str, "-1.#INF"))
     267             :         {
     268           3 :             if (endptr)
     269           1 :                 *endptr = const_cast<char *>(str.data()) + str.size();
     270           3 :             return -std::numeric_limits<double>::infinity();
     271             :         }
     272             :     }
     273     1543710 :     else if (str[0] == '1')
     274             :     {
     275      322107 :         if (starts_with(str, "1.#QNAN") || starts_with(str, "1.#SNAN"))
     276             :         {
     277           2 :             if (endptr)
     278           2 :                 *endptr = const_cast<char *>(str.data()) + str.size();
     279           2 :             return std::numeric_limits<double>::quiet_NaN();
     280             :         }
     281      322105 :         if (starts_with_ci(str, "1.#INF"))
     282             :         {
     283           3 :             if (endptr)
     284           1 :                 *endptr = const_cast<char *>(str.data()) + str.size();
     285           3 :             return std::numeric_limits<double>::infinity();
     286             :         }
     287             :     }
     288             : 
     289             :     // Skip leading '+' as non-handled by fast_float
     290     1717710 :     if (str[0] == '+')
     291        4154 :         str = str.substr(1);
     292             : 
     293             :     // Find a reasonable position for the end of the string to provide to
     294             :     // fast_float
     295     1717710 :     std::ptrdiff_t endPos = 0;
     296    31374400 :     while (endPos < static_cast<std::ptrdiff_t>(str.size()) &&
     297    14913800 :            ((str[endPos] >= '0' && str[endPos] <= '9') ||
     298     1737980 :             str[endPos] == point || str[endPos] == '+' || str[endPos] == '-' ||
     299      275764 :             str[endPos] == 'e' || str[endPos] == 'E'))
     300             :     {
     301    14742900 :         ++endPos;
     302             :     }
     303             : 
     304     1717710 :     double dfValue = 0;
     305             :     const fast_float::parse_options options{fast_float::chars_format::general,
     306     1717710 :                                             point};
     307             :     auto answer = fast_float::from_chars_advanced(
     308     1717710 :         str.data(), str.data() + endPos, dfValue, options);
     309     1717710 :     if (answer.ec != std::errc())
     310             :     {
     311       43699 :         if (
     312             :             // Triggered by ogr_pg tests
     313       43699 :             starts_with_ci(str, "-Infinity"))
     314             :         {
     315          13 :             dfValue = -std::numeric_limits<double>::infinity();
     316          13 :             answer.ptr = str.data() + strlen("-Infinity");
     317             :         }
     318       43686 :         else if (starts_with_ci(str, "-inf"))
     319             :         {
     320          68 :             dfValue = -std::numeric_limits<double>::infinity();
     321          68 :             answer.ptr = str.data() + strlen("-inf");
     322             :         }
     323       43618 :         else if (
     324             :             // Triggered by ogr_pg tests
     325       43618 :             starts_with_ci(str, "Infinity"))
     326             :         {
     327          13 :             dfValue = std::numeric_limits<double>::infinity();
     328          13 :             answer.ptr = str.data() + strlen("Infinity");
     329             :         }
     330       43605 :         else if (cpl::starts_with_ci(str, "inf"))
     331             :         {
     332         136 :             dfValue = std::numeric_limits<double>::infinity();
     333         136 :             answer.ptr = str.data() + strlen("inf");
     334             :         }
     335       43469 :         else if (cpl::starts_with_ci(str, "nan"))
     336             :         {
     337         246 :             dfValue = std::numeric_limits<double>::quiet_NaN();
     338         246 :             answer.ptr = str.data() + strlen("nan");
     339             :         }
     340             :         else
     341             :         {
     342       43223 :             errno = answer.ptr == str.data() ? 0 : ERANGE;
     343             :         }
     344             :     }
     345     1717710 :     if (endptr)
     346             :     {
     347      497077 :         *endptr = const_cast<char *>(answer.ptr);
     348             :     }
     349             : 
     350     1717710 :     return dfValue;
     351             : }
     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     1516020 : double CPLStrtod(const char *nptr, char **endptr)
     376             : {
     377     1516020 :     return CPLStrtodDelim(nptr, endptr, '.');
     378             : }
     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       16632 : double CPLStrtodM(const char *nptr, char **endptr)
     403             : 
     404             : {
     405       16632 :     const int nMaxSearch = 50;
     406             : 
     407       83671 :     for (int i = 0; i < nMaxSearch; i++)
     408             :     {
     409       83671 :         if (nptr[i] == ',')
     410           0 :             return CPLStrtodDelim(nptr, endptr, ',');
     411       83671 :         if (nptr[i] == '.' || nptr[i] == '\0')
     412       16632 :             return CPLStrtodDelim(nptr, endptr, '.');
     413             :     }
     414             : 
     415           0 :     return CPLStrtodDelim(nptr, endptr, '.');
     416             : }
     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          10 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
     440             : {
     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          10 :     char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
     448          10 :     const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
     449          10 :     const float fValue = strtof(pszNumber, endptr);
     450          10 :     const int nError = errno;
     451             : 
     452          10 :     if (endptr)
     453          10 :         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
     454             : 
     455          10 :     if (pszNewNumberOrNull)
     456           0 :         CPLFree(pszNewNumberOrNull);
     457             : 
     458          10 :     errno = nError;
     459          10 :     return fValue;
     460             : }
     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          10 : float CPLStrtof(const char *nptr, char **endptr)
     484             : {
     485          10 :     return CPLStrtofDelim(nptr, endptr, '.');
     486             : }

Generated by: LCOV version 1.14