LCOV - code coverage report
Current view: top level - port - cpl_strtod.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 90 105 85.7 %
Date: 2025-05-24 03:54:53 Functions: 9 9 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             : 
      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             : }

Generated by: LCOV version 1.14