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-04-28 21:03:45 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "cpl_conv.h"
      32             : 
      33             : #include <cerrno>
      34             : #include <clocale>
      35             : #include <cstring>
      36             : #include <cstdlib>
      37             : #include <limits>
      38             : 
      39             : // Coverity complains about CPLAtof(CPLGetConfigOption(...)) causing
      40             : // a "untrusted loop bound" in the loop "Find a reasonable position for the end
      41             : // of the string to provide to fast_float"
      42             : #ifndef __COVERITY__
      43             : #define USE_FAST_FLOAT
      44             : #endif
      45             : 
      46             : #ifdef USE_FAST_FLOAT
      47             : #include "include_fast_float.h"
      48             : #endif
      49             : 
      50             : #include "cpl_config.h"
      51             : 
      52             : /************************************************************************/
      53             : /*                            CPLAtofDelim()                            */
      54             : /************************************************************************/
      55             : 
      56             : /**
      57             :  * Converts ASCII string to floating point number.
      58             :  *
      59             :  * This function converts the initial portion of the string pointed to
      60             :  * by nptr to double floating point representation. The behavior is the
      61             :  * same as
      62             :  *
      63             :  *   CPLStrtodDelim(nptr, (char **)NULL, point);
      64             :  *
      65             :  * This function does the same as standard atof(3), but does not take locale
      66             :  * in account. Instead of locale defined decimal delimiter you can specify
      67             :  * your own one. Also see notes for CPLAtof() function.
      68             :  *
      69             :  * @param nptr Pointer to string to convert.
      70             :  * @param point Decimal delimiter.
      71             :  *
      72             :  * @return Converted value, if any.
      73             :  */
      74      165414 : double CPLAtofDelim(const char *nptr, char point)
      75             : {
      76      165414 :     return CPLStrtodDelim(nptr, nullptr, point);
      77             : }
      78             : 
      79             : /************************************************************************/
      80             : /*                              CPLAtof()                               */
      81             : /************************************************************************/
      82             : 
      83             : /**
      84             :  * Converts ASCII string to floating point number.
      85             :  *
      86             :  * This function converts the initial portion of the string pointed to
      87             :  * by nptr to double floating point representation. The behavior is the
      88             :  * same as
      89             :  *
      90             :  *   CPLStrtod(nptr, (char **)NULL);
      91             :  *
      92             :  * This function does the same as standard atof(3), but does not take
      93             :  * locale in account. That means, the decimal delimiter is always '.'
      94             :  * (decimal point). Use CPLAtofDelim() function if you want to specify
      95             :  * custom delimiter.
      96             :  *
      97             :  * IMPORTANT NOTE:
      98             :  *
      99             :  * Existence of this function does not mean you should always use it.  Sometimes
     100             :  * you should use standard locale aware atof(3) and its family. When you need to
     101             :  * process the user's input (for example, command line parameters) use atof(3),
     102             :  * because the user works in a localized environment and the user's input will
     103             :  * be done according to the locale set. In particular that means we should not
     104             :  * make assumptions about character used as decimal delimiter, it can be either
     105             :  * "." or ",".
     106             :  *
     107             :  * But when you are parsing some ASCII file in predefined format, you most
     108             :  * likely need CPLAtof(), because such files distributed across the systems
     109             :  * with different locales and floating point representation should be
     110             :  * considered as a part of file format. If the format uses "." as a delimiter
     111             :  * the same character must be used when parsing number regardless of actual
     112             :  * locale setting.
     113             :  *
     114             :  * @param nptr Pointer to string to convert.
     115             :  *
     116             :  * @return Converted value, if any.
     117             :  */
     118     1218920 : double CPLAtof(const char *nptr)
     119             : {
     120     1218920 :     return CPLStrtod(nptr, nullptr);
     121             : }
     122             : 
     123             : /************************************************************************/
     124             : /*                              CPLAtofM()                              */
     125             : /************************************************************************/
     126             : 
     127             : /**
     128             :  * Converts ASCII string to floating point number using any numeric locale.
     129             :  *
     130             :  * This function converts the initial portion of the string pointed to
     131             :  * by nptr to double floating point representation. This function does the
     132             :  * same as standard atof(), but it allows a variety of locale representations.
     133             :  * That is it supports numeric values with either a comma or a period for
     134             :  * the decimal delimiter.
     135             :  *
     136             :  * PS. The M stands for Multi-lingual.
     137             :  *
     138             :  * @param nptr The string to convert.
     139             :  *
     140             :  * @return Converted value, if any.  Zero on failure.
     141             :  */
     142             : 
     143       24724 : double CPLAtofM(const char *nptr)
     144             : 
     145             : {
     146       24724 :     const int nMaxSearch = 50;
     147             : 
     148       82046 :     for (int i = 0; i < nMaxSearch; i++)
     149             :     {
     150       82046 :         if (nptr[i] == ',')
     151         412 :             return CPLStrtodDelim(nptr, nullptr, ',');
     152       81634 :         if (nptr[i] == '.' || nptr[i] == '\0')
     153       24312 :             return CPLStrtodDelim(nptr, nullptr, '.');
     154             :     }
     155             : 
     156           0 :     return CPLStrtodDelim(nptr, nullptr, '.');
     157             : }
     158             : 
     159             : /************************************************************************/
     160             : /*                      CPLReplacePointByLocalePoint()                  */
     161             : /************************************************************************/
     162             : 
     163             : /* Return a newly allocated variable if substitution was done, or NULL
     164             :  * otherwise.
     165             :  */
     166           0 : static char *CPLReplacePointByLocalePoint(const char *pszNumber, char point)
     167             : {
     168             : #if defined(__ANDROID__) && __ANDROID_API__ < 20
     169             :     // localeconv() only available since API 20
     170             :     static char byPoint = 0;
     171             :     if (byPoint == 0)
     172             :     {
     173             :         char szBuf[16] = {};
     174             :         snprintf(szBuf, sizeof(szBuf), "%.1f", 1.0);
     175             :         byPoint = szBuf[1];
     176             :     }
     177             :     if (point != byPoint)
     178             :     {
     179             :         const char *pszPoint = strchr(pszNumber, point);
     180             :         if (pszPoint)
     181             :         {
     182             :             char *pszNew = CPLStrdup(pszNumber);
     183             :             pszNew[pszPoint - pszNumber] = byPoint;
     184             :             return pszNew;
     185             :         }
     186             :     }
     187             : #else   // ndef __ANDROID__
     188           0 :     struct lconv *poLconv = localeconv();
     189           0 :     if (poLconv && poLconv->decimal_point && poLconv->decimal_point[0] != '\0')
     190             :     {
     191           0 :         char byPoint = poLconv->decimal_point[0];
     192             : 
     193           0 :         if (point != byPoint)
     194             :         {
     195           0 :             const char *pszLocalePoint = strchr(pszNumber, byPoint);
     196           0 :             const char *pszPoint = strchr(pszNumber, point);
     197           0 :             if (pszPoint || pszLocalePoint)
     198             :             {
     199           0 :                 char *pszNew = CPLStrdup(pszNumber);
     200           0 :                 if (pszLocalePoint)
     201           0 :                     pszNew[pszLocalePoint - pszNumber] = ' ';
     202           0 :                 if (pszPoint)
     203           0 :                     pszNew[pszPoint - pszNumber] = byPoint;
     204           0 :                 return pszNew;
     205             :             }
     206             :         }
     207             :     }
     208             : #endif  // __ANDROID__
     209             : 
     210           0 :     return nullptr;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                          CPLStrtodDelim()                            */
     215             : /************************************************************************/
     216             : 
     217             : /**
     218             :  * Converts ASCII string to floating point number using specified delimiter.
     219             :  *
     220             :  * This function converts the initial portion of the string pointed to
     221             :  * by nptr to double floating point representation. This function does the
     222             :  * same as standard strtod(3), but does not take locale in account. Instead of
     223             :  * locale defined decimal delimiter you can specify your own one. Also see
     224             :  * notes for CPLAtof() function.
     225             :  *
     226             :  * @param nptr Pointer to string to convert.
     227             :  * @param endptr If is not NULL, a pointer to the character after the last
     228             :  * character used in the conversion is stored in the location referenced
     229             :  * by endptr.
     230             :  * @param point Decimal delimiter.
     231             :  *
     232             :  * @return Converted value, if any.
     233             :  */
     234     1653090 : double CPLStrtodDelim(const char *nptr, char **endptr, char point)
     235             : {
     236     1735410 :     while (*nptr == ' '
     237             : #ifdef USE_FAST_FLOAT
     238             :            // The GSAG driver provides leading end-of-line character
     239     1653090 :            || *nptr == '\r' || *nptr == '\n' || *nptr == '\t'
     240             : #endif
     241             :     )
     242             :     {
     243       82325 :         nptr++;
     244             :     }
     245             : 
     246     1570760 :     if (nptr[0] == '-')
     247             :     {
     248      215472 :         if (STARTS_WITH(nptr, "-1.#QNAN") || STARTS_WITH(nptr, "-1.#IND"))
     249             :         {
     250           2 :             if (endptr)
     251           2 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     252             :             // While it is possible on some platforms to flip the sign
     253             :             // of NAN to negative, this function will always return a positive
     254             :             // quiet (non-signalling) NaN.
     255           2 :             return std::numeric_limits<double>::quiet_NaN();
     256             :         }
     257      215470 :         if (
     258             : #ifndef USE_FAST_FLOAT
     259             :             strcmp(nptr, "-inf") == 0 ||
     260             : #endif
     261      215470 :             STARTS_WITH_CI(nptr, "-1.#INF"))
     262             :         {
     263           2 :             if (endptr)
     264           1 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     265           2 :             return -std::numeric_limits<double>::infinity();
     266             :         }
     267             :     }
     268     1355290 :     else if (nptr[0] == '1')
     269             :     {
     270      260473 :         if (STARTS_WITH(nptr, "1.#QNAN") || STARTS_WITH(nptr, "1.#SNAN"))
     271             :         {
     272           2 :             if (endptr)
     273           2 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     274           2 :             return std::numeric_limits<double>::quiet_NaN();
     275             :         }
     276      260471 :         if (STARTS_WITH_CI(nptr, "1.#INF"))
     277             :         {
     278           2 :             if (endptr)
     279           1 :                 *endptr = const_cast<char *>(nptr) + strlen(nptr);
     280           2 :             return std::numeric_limits<double>::infinity();
     281             :         }
     282             :     }
     283             : #ifndef USE_FAST_FLOAT
     284             :     else if (nptr[0] == 'i' && strcmp(nptr, "inf") == 0)
     285             :     {
     286             :         if (endptr)
     287             :             *endptr = const_cast<char *>(nptr) + strlen(nptr);
     288             :         return std::numeric_limits<double>::infinity();
     289             :     }
     290             :     else if (nptr[0] == 'n' && strcmp(nptr, "nan") == 0)
     291             :     {
     292             :         if (endptr)
     293             :             *endptr = const_cast<char *>(nptr) + strlen(nptr);
     294             :         return std::numeric_limits<double>::quiet_NaN();
     295             :     }
     296             : #endif
     297             : 
     298             : #ifdef USE_FAST_FLOAT
     299             :     // Skip leading '+' as non-handled by fast_float
     300     1570750 :     if (*nptr == '+')
     301        4138 :         nptr++;
     302             : 
     303             :     // Find a reasonable position for the end of the string to provide to
     304             :     // fast_float
     305     1570750 :     const char *endptrIn = nptr;
     306    16183800 :     while ((*endptrIn >= '0' && *endptrIn <= '9') || *endptrIn == point ||
     307    18050600 :            *endptrIn == '+' || *endptrIn == '-' || *endptrIn == 'e' ||
     308     1601220 :            *endptrIn == 'E')
     309             :     {
     310    14613000 :         ++endptrIn;
     311             :     }
     312             : 
     313     1570750 :     double dfValue = 0;
     314             :     const fast_float::parse_options options{fast_float::chars_format::general,
     315     1570750 :                                             point};
     316             :     auto answer =
     317     1570750 :         fast_float::from_chars_advanced(nptr, endptrIn, dfValue, options);
     318     1570750 :     if (answer.ec != std::errc())
     319             :     {
     320       41095 :         if (strcmp(nptr, "-inf") == 0)
     321             :         {
     322          58 :             dfValue = -std::numeric_limits<double>::infinity();
     323          58 :             answer.ptr = nptr + strlen("-inf");
     324             :         }
     325       41037 :         else if (  // Generated by SQLite (impacts ogr_gpkg tests)
     326       41037 :             strcmp(nptr, "-Inf") == 0)
     327             :         {
     328           4 :             dfValue = -std::numeric_limits<double>::infinity();
     329           4 :             answer.ptr = nptr + strlen("-Inf");
     330             :         }
     331       41033 :         else if (
     332             :             // Reported by user as being understood in previous GDAL versions
     333       41033 :             strcmp(nptr, "-INF") == 0)
     334             :         {
     335           1 :             dfValue = -std::numeric_limits<double>::infinity();
     336           1 :             answer.ptr = nptr + strlen("-INF");
     337             :         }
     338       41032 :         else if (
     339             :             // Triggered by ogr_pg tests
     340       41032 :             strcmp(nptr, "-Infinity") == 0)
     341             :         {
     342          17 :             dfValue = -std::numeric_limits<double>::infinity();
     343          17 :             answer.ptr = nptr + strlen("-Infinity");
     344             :         }
     345       41015 :         else if (strcmp(nptr, "inf") == 0)
     346             :         {
     347         106 :             dfValue = std::numeric_limits<double>::infinity();
     348         106 :             answer.ptr = nptr + strlen("inf");
     349             :         }
     350       40909 :         else if (  // Generated by SQLite (impacts ogr_gpkg tests)
     351       40909 :             strcmp(nptr, "Inf") == 0)
     352             :         {
     353           4 :             dfValue = std::numeric_limits<double>::infinity();
     354           4 :             answer.ptr = nptr + strlen("Inf");
     355             :         }
     356       40905 :         else if (
     357             :             // Reported by user as being understood in previous GDAL versions
     358       40905 :             strcmp(nptr, "INF") == 0)
     359             :         {
     360           1 :             dfValue = std::numeric_limits<double>::infinity();
     361           1 :             answer.ptr = nptr + strlen("INF");
     362             :         }
     363       40904 :         else if (
     364             :             // Triggered by ogr_pg tests
     365       40904 :             strcmp(nptr, "Infinity") == 0)
     366             :         {
     367          13 :             dfValue = std::numeric_limits<double>::infinity();
     368          13 :             answer.ptr = nptr + strlen("Infinity");
     369             :         }
     370       40891 :         else if (strcmp(nptr, "nan") == 0)
     371             :         {
     372         112 :             dfValue = std::numeric_limits<double>::quiet_NaN();
     373         112 :             answer.ptr = nptr + strlen("nan");
     374             :         }
     375       40779 :         else if (
     376             :             // Triggered by ogr_pg tests
     377       40779 :             strcmp(nptr, "NaN") == 0)
     378             :         {
     379          14 :             dfValue = std::numeric_limits<double>::quiet_NaN();
     380          14 :             answer.ptr = nptr + strlen("NaN");
     381             :         }
     382             :         else
     383             :         {
     384       40765 :             errno = answer.ptr == nptr ? 0 : ERANGE;
     385             :         }
     386             :     }
     387     1570750 :     if (endptr)
     388             :     {
     389      158259 :         *endptr = const_cast<char *>(answer.ptr);
     390             :     }
     391             : #else
     392             :     /* -------------------------------------------------------------------- */
     393             :     /*  We are implementing a simple method here: copy the input string     */
     394             :     /*  into the temporary buffer, replace the specified decimal delimiter  */
     395             :     /*  with the one, taken from locale settings and use standard strtod()  */
     396             :     /*  on that buffer.                                                     */
     397             :     /* -------------------------------------------------------------------- */
     398             :     char *pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
     399             :     const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
     400             : 
     401             :     const double dfValue = strtod(pszNumber, endptr);
     402             :     const int nError = errno;
     403             : 
     404             :     if (endptr)
     405             :         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
     406             : 
     407             :     if (pszNewNumberOrNull)
     408             :         CPLFree(pszNewNumberOrNull);
     409             : 
     410             :     errno = nError;
     411             : #endif
     412             : 
     413     1570750 :     return dfValue;
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*                             CPLStrtod()                              */
     418             : /************************************************************************/
     419             : 
     420             : /**
     421             :  * Converts ASCII string to floating point number.
     422             :  *
     423             :  * This function converts the initial portion of the string pointed to
     424             :  * by nptr to double floating point representation. This function does the
     425             :  * same as standard strtod(3), but does not take locale in account. That
     426             :  * means, the decimal delimiter is always '.' (decimal point). Use
     427             :  * CPLStrtodDelim() function if you want to specify custom delimiter. Also
     428             :  * see notes for CPLAtof() function.
     429             :  *
     430             :  * @param nptr Pointer to string to convert.
     431             :  * @param endptr If is not NULL, a pointer to the character after the last
     432             :  * character used in the conversion is stored in the location referenced
     433             :  * by endptr.
     434             :  *
     435             :  * @return Converted value, if any.
     436             :  */
     437     1378510 : double CPLStrtod(const char *nptr, char **endptr)
     438             : {
     439     1378510 :     return CPLStrtodDelim(nptr, endptr, '.');
     440             : }
     441             : 
     442             : /************************************************************************/
     443             : /*                            CPLStrtodM()                              */
     444             : /************************************************************************/
     445             : 
     446             : /**
     447             :  * Converts ASCII string to floating point number.
     448             :  *
     449             :  * This function converts the initial portion of the string pointed to
     450             :  * by nptr to double floating point representation. This function does the
     451             :  * same as standard strtod(3), but does not take locale in account.
     452             :  *
     453             :  * That function accepts '.' (decimal point) or ',' (comma) as decimal
     454             :  * delimiter.
     455             :  *
     456             :  * @param nptr Pointer to string to convert.
     457             :  * @param endptr If is not NULL, a pointer to the character after the last
     458             :  * character used in the conversion is stored in the location referenced
     459             :  * by endptr.
     460             :  *
     461             :  * @return Converted value, if any.
     462             :  * @since GDAL 3.9
     463             :  */
     464        2116 : double CPLStrtodM(const char *nptr, char **endptr)
     465             : 
     466             : {
     467        2116 :     const int nMaxSearch = 50;
     468             : 
     469       11566 :     for (int i = 0; i < nMaxSearch; i++)
     470             :     {
     471       11566 :         if (nptr[i] == ',')
     472           0 :             return CPLStrtodDelim(nptr, endptr, ',');
     473       11566 :         if (nptr[i] == '.' || nptr[i] == '\0')
     474        2116 :             return CPLStrtodDelim(nptr, endptr, '.');
     475             :     }
     476             : 
     477           0 :     return CPLStrtodDelim(nptr, endptr, '.');
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                          CPLStrtofDelim()                            */
     482             : /************************************************************************/
     483             : 
     484             : /**
     485             :  * Converts ASCII string to floating point number using specified delimiter.
     486             :  *
     487             :  * This function converts the initial portion of the string pointed to
     488             :  * by nptr to single floating point representation. This function does the
     489             :  * same as standard strtof(3), but does not take locale in account. Instead of
     490             :  * locale defined decimal delimiter you can specify your own one. Also see
     491             :  * notes for CPLAtof() function.
     492             :  *
     493             :  * @param nptr Pointer to string to convert.
     494             :  * @param endptr If is not NULL, a pointer to the character after the last
     495             :  * character used in the conversion is stored in the location referenced
     496             :  * by endptr.
     497             :  * @param point Decimal delimiter.
     498             :  *
     499             :  * @return Converted value, if any.
     500             :  */
     501           0 : float CPLStrtofDelim(const char *nptr, char **endptr, char point)
     502             : {
     503             :     /* -------------------------------------------------------------------- */
     504             :     /*  We are implementing a simple method here: copy the input string     */
     505             :     /*  into the temporary buffer, replace the specified decimal delimiter  */
     506             :     /*  with the one, taken from locale settings and use standard strtof()  */
     507             :     /*  on that buffer.                                                     */
     508             :     /* -------------------------------------------------------------------- */
     509           0 :     char *const pszNewNumberOrNull = CPLReplacePointByLocalePoint(nptr, point);
     510           0 :     const char *pszNumber = pszNewNumberOrNull ? pszNewNumberOrNull : nptr;
     511           0 :     const float fValue = strtof(pszNumber, endptr);
     512           0 :     const int nError = errno;
     513             : 
     514           0 :     if (endptr)
     515           0 :         *endptr = const_cast<char *>(nptr) + (*endptr - pszNumber);
     516             : 
     517           0 :     if (pszNewNumberOrNull)
     518           0 :         CPLFree(pszNewNumberOrNull);
     519             : 
     520           0 :     errno = nError;
     521           0 :     return fValue;
     522             : }
     523             : 
     524             : /************************************************************************/
     525             : /*                             CPLStrtof()                              */
     526             : /************************************************************************/
     527             : 
     528             : /**
     529             :  * Converts ASCII string to floating point number.
     530             :  *
     531             :  * This function converts the initial portion of the string pointed to
     532             :  * by nptr to single floating point representation. This function does the
     533             :  * same as standard strtof(3), but does not take locale in account. That
     534             :  * means, the decimal delimiter is always '.' (decimal point). Use
     535             :  * CPLStrtofDelim() function if you want to specify custom delimiter. Also
     536             :  * see notes for CPLAtof() function.
     537             :  *
     538             :  * @param nptr Pointer to string to convert.
     539             :  * @param endptr If is not NULL, a pointer to the character after the last
     540             :  * character used in the conversion is stored in the location referenced
     541             :  * by endptr.
     542             :  *
     543             :  * @return Converted value, if any.
     544             :  */
     545           0 : float CPLStrtof(const char *nptr, char **endptr)
     546             : {
     547           0 :     return CPLStrtofDelim(nptr, endptr, '.');
     548             : }

Generated by: LCOV version 1.14