LCOV - code coverage report
Current view: top level - port - cpl_strtod.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 94 125 75.2 %
Date: 2024-11-21 22:18:42 Functions: 6 9 66.7 %

          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      165496 : double CPLAtofDelim(const char *nptr, char point)
      59             : {
      60      165496 :     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    14717400 : double CPLAtof(const char *nptr)
     103             : {
     104    14717400 :     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       24799 : double CPLAtofM(const char *nptr)
     128             : 
     129             : {
     130       24799 :     const int nMaxSearch = 50;
     131             : 
     132       82144 :     for (int i = 0; i < nMaxSearch; i++)
     133             :     {
     134       82144 :         if (nptr[i] == ',')
     135         412 :             return CPLStrtodDelim(nptr, nullptr, ',');
     136       81732 :         if (nptr[i] == '.' || nptr[i] == '\0')
     137       24387 :             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           0 : 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           0 :     struct lconv *poLconv = localeconv();
     173           0 :     if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
     174             :     {
     175           0 :         char byPoint = poLconv->decimal_point[0];
     176             : 
     177           0 :         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           0 :     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    15359300 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
     219             : {
     220    15442900 :     while (*nptr == ' '
     221             : #ifdef USE_FAST_FLOAT
     222             :            // The GSAG driver provides leading end-of-line character
     223    15359300 :            || *nptr == '\r' || *nptr == '\n' || *nptr == '\t'
     224             : #endif
     225             :     )
     226             :     {
     227       83603 :         nptr++;
     228             :     }
     229             : 
     230    15275700 :     if (nptr[0] == '-')
     231             :     {
     232     6542760 :         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     6542760 :         if (
     242             : #ifndef USE_FAST_FLOAT
     243             :             strcmp(nptr, "-inf") == 0 ||
     244             : #endif
     245     6542760 :             STARTS_WITH_CI(nptr, "-1.#INF"))
     246             :         {
     247           2 :             if (endptr)
     248           1 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     249           2 :             return -std::numeric_limits<double>::infinity();
     250             :         }
     251             :     }
     252     8732980 :     else if (nptr[0] == '1')
     253             :     {
     254     2048410 :         if (STARTS_WITH(nptr, "1.#QNAN") || STARTS_WITH(nptr, "1.#SNAN"))
     255             :         {
     256           2 :             if (endptr)
     257           2 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     258           2 :             return std::numeric_limits<double>::quiet_NaN();
     259             :         }
     260     2048410 :         if (STARTS_WITH_CI(nptr, "1.#INF"))
     261             :         {
     262           2 :             if (endptr)
     263           1 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     264           2 :             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    15275700 :     if (*nptr == '+')
     285        4124 :         nptr++;
     286             : 
     287             :     // Find a reasonable position for the end of the string to provide to
     288             :     // fast_float
     289    15275700 :     const char *endptrIn = nptr;
     290   126892000 :     while ((*endptrIn >= '0' && *endptrIn <= '9') || *endptrIn == point ||
     291   148927000 :            *endptrIn == '+' || *endptrIn == '-' || *endptrIn == 'e' ||
     292    15371700 :            *endptrIn == 'E')
     293             :     {
     294   111616000 :         ++endptrIn;
     295             :     }
     296             : 
     297    15275700 :     double dfValue = 0;
     298             :     const fast_float::parse_options options{fast_float::chars_format::general,
     299    15275700 :                                             point};
     300             :     auto answer =
     301    15275700 :         fast_float::from_chars_advanced(nptr, endptrIn, dfValue, options);
     302    15275700 :     if (answer.ec != std::errc())
     303             :     {
     304       41775 :         if (strcmp(nptr, "-inf") == 0)
     305             :         {
     306          63 :             dfValue = -std::numeric_limits<double>::infinity();
     307          63 :             answer.ptr = nptr + strlen("-inf");
     308             :         }
     309       41712 :         else if (  // Generated by SQLite (impacts ogr_gpkg tests)
     310       41712 :             strcmp(nptr, "-Inf") == 0)
     311             :         {
     312           4 :             dfValue = -std::numeric_limits<double>::infinity();
     313           4 :             answer.ptr = nptr + strlen("-Inf");
     314             :         }
     315       41708 :         else if (
     316             :             // Reported by user as being understood in previous GDAL versions
     317       41708 :             strcmp(nptr, "-INF") == 0)
     318             :         {
     319           1 :             dfValue = -std::numeric_limits<double>::infinity();
     320           1 :             answer.ptr = nptr + strlen("-INF");
     321             :         }
     322       41707 :         else if (
     323             :             // Triggered by ogr_pg tests
     324       41707 :             strcmp(nptr, "-Infinity") == 0)
     325             :         {
     326          17 :             dfValue = -std::numeric_limits<double>::infinity();
     327          17 :             answer.ptr = nptr + strlen("-Infinity");
     328             :         }
     329       41690 :         else if (strcmp(nptr, "inf") == 0)
     330             :         {
     331         107 :             dfValue = std::numeric_limits<double>::infinity();
     332         107 :             answer.ptr = nptr + strlen("inf");
     333             :         }
     334       41583 :         else if (  // Generated by SQLite (impacts ogr_gpkg tests)
     335       41583 :             strcmp(nptr, "Inf") == 0)
     336             :         {
     337           4 :             dfValue = std::numeric_limits<double>::infinity();
     338           4 :             answer.ptr = nptr + strlen("Inf");
     339             :         }
     340       41579 :         else if (
     341             :             // Reported by user as being understood in previous GDAL versions
     342       41579 :             strcmp(nptr, "INF") == 0)
     343             :         {
     344           1 :             dfValue = std::numeric_limits<double>::infinity();
     345           1 :             answer.ptr = nptr + strlen("INF");
     346             :         }
     347       41578 :         else if (
     348             :             // Triggered by ogr_pg tests
     349       41578 :             strcmp(nptr, "Infinity") == 0)
     350             :         {
     351          13 :             dfValue = std::numeric_limits<double>::infinity();
     352          13 :             answer.ptr = nptr + strlen("Infinity");
     353             :         }
     354       41565 :         else if (strcmp(nptr, "nan") == 0)
     355             :         {
     356         116 :             dfValue = std::numeric_limits<double>::quiet_NaN();
     357         116 :             answer.ptr = nptr + strlen("nan");
     358             :         }
     359       41449 :         else if (
     360             :             // Triggered by ogr_pg tests
     361       41449 :             strcmp(nptr, "NaN") == 0)
     362             :         {
     363          15 :             dfValue = std::numeric_limits<double>::quiet_NaN();
     364          15 :             answer.ptr = nptr + strlen("NaN");
     365             :         }
     366             :         else
     367             :         {
     368       41434 :             errno = answer.ptr == nptr ? 0 : ERANGE;
     369             :         }
     370             :     }
     371    15275700 :     if (endptr)
     372             :     {
     373      364476 :         *endptr = const_cast<char *>(answer.ptr);
     374             :     }
     375             : #else
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*  We are implementing a simple method here: copy the input string     */
     378             :     /*  into the temporary buffer, replace the specified decimal delimiter  */
     379             :     /*  with the one, taken from locale settings and use standard strtod()  */
     380             :     /*  on that buffer.                                                     */
     381             :     /* -------------------------------------------------------------------- */
     382             :     char *pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
     383             :     const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
     384             : 
     385             :     const double dfValue = strtod(pszNumber, endptr);
     386             :     const int nError = errno;
     387             : 
     388             :     if (endptr)
     389             :         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
     390             : 
     391             :     if (pszNewNumberOrNull)
     392             :         CPLFree(pszNewNumberOrNull);
     393             : 
     394             :     errno = nError;
     395             : #endif
     396             : 
     397    15275700 :     return dfValue;
     398             : }
     399             : 
     400             : /************************************************************************/
     401             : /*                             CPLStrtod()                              */
     402             : /************************************************************************/
     403             : 
     404             : /**
     405             :  * Converts ASCII string to floating point number.
     406             :  *
     407             :  * This function converts the initial portion of the string pointed to
     408             :  * by nptr to double floating point representation. This function does the
     409             :  * same as standard strtod(3), but does not take locale in account. That
     410             :  * means, the decimal delimiter is always '.' (decimal point). Use
     411             :  * CPLStrtodDelim() function if you want to specify custom delimiter. Also
     412             :  * see notes for CPLAtof() function.
     413             :  *
     414             :  * @param nptr Pointer to string to convert.
     415             :  * @param endptr If is not NULL, a pointer to the character after the last
     416             :  * character used in the conversion is stored in the location referenced
     417             :  * by endptr.
     418             :  *
     419             :  * @return Converted value, if any.
     420             :  */
     421    15076100 : double CPLStrtod(const char *nptr, char **endptr)
     422             : {
     423    15076100 :     return CPLStrtodDelim(nptr, endptr, '.');
     424             : }
     425             : 
     426             : /************************************************************************/
     427             : /*                            CPLStrtodM()                              */
     428             : /************************************************************************/
     429             : 
     430             : /**
     431             :  * Converts ASCII string to floating point number.
     432             :  *
     433             :  * This function converts the initial portion of the string pointed to
     434             :  * by nptr to double floating point representation. This function does the
     435             :  * same as standard strtod(3), but does not take locale in account.
     436             :  *
     437             :  * That function accepts '.' (decimal point) or ',' (comma) as decimal
     438             :  * delimiter.
     439             :  *
     440             :  * @param nptr Pointer to string to convert.
     441             :  * @param endptr If is not NULL, a pointer to the character after the last
     442             :  * character used in the conversion is stored in the location referenced
     443             :  * by endptr.
     444             :  *
     445             :  * @return Converted value, if any.
     446             :  * @since GDAL 3.9
     447             :  */
     448        6249 : double CPLStrtodM(const char *nptr, char **endptr)
     449             : 
     450             : {
     451        6249 :     const int nMaxSearch = 50;
     452             : 
     453       26535 :     for (int i = 0; i < nMaxSearch; i++)
     454             :     {
     455       26535 :         if (nptr[i] == ',')
     456           0 :             return CPLStrtodDelim(nptr, endptr, ',');
     457       26535 :         if (nptr[i] == '.' || nptr[i] == '\0')
     458        6249 :             return CPLStrtodDelim(nptr, endptr, '.');
     459             :     }
     460             : 
     461           0 :     return CPLStrtodDelim(nptr, endptr, '.');
     462             : }
     463             : 
     464             : /************************************************************************/
     465             : /*                          CPLStrtofDelim()                            */
     466             : /************************************************************************/
     467             : 
     468             : /**
     469             :  * Converts ASCII string to floating point number using specified delimiter.
     470             :  *
     471             :  * This function converts the initial portion of the string pointed to
     472             :  * by nptr to single floating point representation. This function does the
     473             :  * same as standard strtof(3), but does not take locale in account. Instead of
     474             :  * locale defined decimal delimiter you can specify your own one. Also see
     475             :  * notes for CPLAtof() function.
     476             :  *
     477             :  * @param nptr Pointer to string to convert.
     478             :  * @param endptr If is not NULL, a pointer to the character after the last
     479             :  * character used in the conversion is stored in the location referenced
     480             :  * by endptr.
     481             :  * @param point Decimal delimiter.
     482             :  *
     483             :  * @return Converted value, if any.
     484             :  */
     485           0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
     486             : {
     487             :     /* -------------------------------------------------------------------- */
     488             :     /*  We are implementing a simple method here: copy the input string     */
     489             :     /*  into the temporary buffer, replace the specified decimal delimiter  */
     490             :     /*  with the one, taken from locale settings and use standard strtof()  */
     491             :     /*  on that buffer.                                                     */
     492             :     /* -------------------------------------------------------------------- */
     493           0 :     char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
     494           0 :     const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
     495           0 :     const float fValue = strtof(pszNumber, endptr);
     496           0 :     const int nError = errno;
     497             : 
     498           0 :     if (endptr)
     499           0 :         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
     500             : 
     501           0 :     if (pszNewNumberOrNull)
     502           0 :         CPLFree(pszNewNumberOrNull);
     503             : 
     504           0 :     errno = nError;
     505           0 :     return fValue;
     506             : }
     507             : 
     508             : /************************************************************************/
     509             : /*                             CPLStrtof()                              */
     510             : /************************************************************************/
     511             : 
     512             : /**
     513             :  * Converts ASCII string to floating point number.
     514             :  *
     515             :  * This function converts the initial portion of the string pointed to
     516             :  * by nptr to single floating point representation. This function does the
     517             :  * same as standard strtof(3), but does not take locale in account. That
     518             :  * means, the decimal delimiter is always '.' (decimal point). Use
     519             :  * CPLStrtofDelim() function if you want to specify custom delimiter. Also
     520             :  * see notes for CPLAtof() function.
     521             :  *
     522             :  * @param nptr Pointer to string to convert.
     523             :  * @param endptr If is not NULL, a pointer to the character after the last
     524             :  * character used in the conversion is stored in the location referenced
     525             :  * by endptr.
     526             :  *
     527             :  * @return Converted value, if any.
     528             :  */
     529           0 : float CPLStrtof(const char *nptr, char **endptr)
     530             : {
     531           0 :     return CPLStrtofDelim(nptr, endptr, '.');
     532             : }

Generated by: LCOV version 1.14