LCOV - code coverage report
Current view: top level - third_party/fast_float - parse_number.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 54 87 62.1 %
Date: 2024-11-21 22:18:42 Functions: 5 5 100.0 %

          Line data    Source code
       1             : #ifndef FASTFLOAT_PARSE_NUMBER_H
       2             : #define FASTFLOAT_PARSE_NUMBER_H
       3             : 
       4             : #include "ascii_number.h"
       5             : #include "decimal_to_binary.h"
       6             : #include "digit_comparison.h"
       7             : #include "float_common.h"
       8             : 
       9             : #include <cmath>
      10             : #include <cstring>
      11             : #include <limits>
      12             : #include <system_error>
      13             : namespace fast_float {
      14             : 
      15             : namespace detail {
      16             : /**
      17             :  * Special case +inf, -inf, nan, infinity, -infinity.
      18             :  * The case comparisons could be made much faster given that we know that the
      19             :  * strings a null-free and fixed.
      20             :  **/
      21             : template <typename T, typename UC>
      22         662 : from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
      23             :                                                            UC const *last,
      24             :                                                            T &value) noexcept {
      25         662 :   from_chars_result_t<UC> answer{};
      26         662 :   answer.ptr = first;
      27         662 :   answer.ec = std::errc(); // be optimistic
      28         662 :   bool minusSign = false;
      29         662 :   if (*first ==
      30             :       UC('-')) { // assume first < last, so dereference without checks;
      31             :                  // C++17 20.19.3.(7.1) explicitly forbids '+' here
      32         252 :     minusSign = true;
      33         252 :     ++first;
      34             :   }
      35             : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
      36             :   if (*first == UC('+')) {
      37             :     ++first;
      38             :   }
      39             : #endif
      40         662 :   if (last - first >= 3) {
      41           5 :     if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
      42           0 :       answer.ptr = (first += 3);
      43           0 :       value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
      44           0 :                         : std::numeric_limits<T>::quiet_NaN();
      45             :       // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
      46             :       // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
      47           0 :       if (first != last && *first == UC('(')) {
      48           0 :         for (UC const *ptr = first + 1; ptr != last; ++ptr) {
      49           0 :           if (*ptr == UC(')')) {
      50           0 :             answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
      51           0 :             break;
      52           0 :           } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) ||
      53           0 :                        (UC('A') <= *ptr && *ptr <= UC('Z')) ||
      54           0 :                        (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
      55           0 :             break; // forbidden char, not nan(n-char-seq-opt)
      56             :         }
      57             :       }
      58           0 :       return answer;
      59             :     }
      60           5 :     if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
      61           0 :       if ((last - first >= 8) &&
      62           0 :           fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
      63           0 :         answer.ptr = first + 8;
      64             :       } else {
      65           0 :         answer.ptr = first + 3;
      66             :       }
      67           0 :       value = minusSign ? -std::numeric_limits<T>::infinity()
      68           0 :                         : std::numeric_limits<T>::infinity();
      69           0 :       return answer;
      70             :     }
      71             :   }
      72         662 :   answer.ec = std::errc::invalid_argument;
      73         662 :   return answer;
      74             : }
      75             : 
      76             : /**
      77             :  * Returns true if the floating-pointing rounding mode is to 'nearest'.
      78             :  * It is the default on most system. This function is meant to be inexpensive.
      79             :  * Credit : @mwalcott3
      80             :  */
      81             : fastfloat_really_inline bool rounds_to_nearest() noexcept {
      82             :   // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
      83             : #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
      84             :   return false;
      85             : #endif
      86             :   // See
      87             :   // A fast function to check your floating-point rounding mode
      88             :   // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
      89             :   //
      90             :   // This function is meant to be equivalent to :
      91             :   // prior: #include <cfenv>
      92             :   //  return fegetround() == FE_TONEAREST;
      93             :   // However, it is expected to be much faster than the fegetround()
      94             :   // function call.
      95             :   //
      96             :   // The volatile keywoard prevents the compiler from computing the function
      97             :   // at compile-time.
      98             :   // There might be other ways to prevent compile-time optimizations (e.g.,
      99             :   // asm). The value does not need to be std::numeric_limits<float>::min(), any
     100             :   // small value so that 1 + x should round to 1 would do (after accounting for
     101             :   // excess precision, as in 387 instructions).
     102             :   static volatile float fmin = std::numeric_limits<float>::min();
     103    15170400 :   float fmini = fmin; // we copy it so that it gets loaded at most once.
     104             : //
     105             : // Explanation:
     106             : // Only when fegetround() == FE_TONEAREST do we have that
     107             : // fmin + 1.0f == 1.0f - fmin.
     108             : //
     109             : // FE_UPWARD:
     110             : //  fmin + 1.0f > 1
     111             : //  1.0f - fmin == 1
     112             : //
     113             : // FE_DOWNWARD or  FE_TOWARDZERO:
     114             : //  fmin + 1.0f == 1
     115             : //  1.0f - fmin < 1
     116             : //
     117             : // Note: This may fail to be accurate if fast-math has been
     118             : // enabled, as rounding conventions may not apply.
     119             : #ifdef FASTFLOAT_VISUAL_STUDIO
     120             : #pragma warning(push)
     121             : //  todo: is there a VS warning?
     122             : //  see
     123             : //  https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
     124             : #elif defined(__clang__)
     125             : #pragma clang diagnostic push
     126             : #pragma clang diagnostic ignored "-Wfloat-equal"
     127             : #elif defined(__GNUC__)
     128             : #pragma GCC diagnostic push
     129             : #pragma GCC diagnostic ignored "-Wfloat-equal"
     130             : #endif
     131    15170400 :   return (fmini + 1.0f == 1.0f - fmini);
     132             : #ifdef FASTFLOAT_VISUAL_STUDIO
     133             : #pragma warning(pop)
     134             : #elif defined(__clang__)
     135             : #pragma clang diagnostic pop
     136             : #elif defined(__GNUC__)
     137             : #pragma GCC diagnostic pop
     138             : #endif
     139             : }
     140             : 
     141             : } // namespace detail
     142             : 
     143             : template <typename T> struct from_chars_caller {
     144             :   template <typename UC>
     145             :   FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
     146         224 :   call(UC const *first, UC const *last, T &value,
     147             :        parse_options_t<UC> options) noexcept {
     148         224 :     return from_chars_advanced(first, last, value, options);
     149             :   }
     150             : };
     151             : 
     152             : #if __STDCPP_FLOAT32_T__ == 1
     153             : template <> struct from_chars_caller<std::float32_t> {
     154             :   template <typename UC>
     155             :   FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
     156             :   call(UC const *first, UC const *last, std::float32_t &value,
     157             :        parse_options_t<UC> options) noexcept {
     158             :     // if std::float32_t is defined, and we are in C++23 mode; macro set for
     159             :     // float32; set value to float due to equivalence between float and
     160             :     // float32_t
     161             :     float val;
     162             :     auto ret = from_chars_advanced(first, last, val, options);
     163             :     value = val;
     164             :     return ret;
     165             :   }
     166             : };
     167             : #endif
     168             : 
     169             : #if __STDCPP_FLOAT64_T__ == 1
     170             : template <> struct from_chars_caller<std::float64_t> {
     171             :   template <typename UC>
     172             :   FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
     173             :   call(UC const *first, UC const *last, std::float64_t &value,
     174             :        parse_options_t<UC> options) noexcept {
     175             :     // if std::float64_t is defined, and we are in C++23 mode; macro set for
     176             :     // float64; set value as double due to equivalence between double and
     177             :     // float64_t
     178             :     double val;
     179             :     auto ret = from_chars_advanced(first, last, val, options);
     180             :     value = val;
     181             :     return ret;
     182             :   }
     183             : };
     184             : #endif
     185             : 
     186             : template <typename T, typename UC, typename>
     187             : FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
     188         224 : from_chars(UC const *first, UC const *last, T &value,
     189             :            chars_format fmt /*= chars_format::general*/) noexcept {
     190         448 :   return from_chars_caller<T>::call(first, last, value,
     191         224 :                                     parse_options_t<UC>(fmt));
     192             : }
     193             : 
     194             : /**
     195             :  * This function overload takes parsed_number_string_t structure that is created
     196             :  * and populated either by from_chars_advanced function taking chars range and
     197             :  * parsing options or other parsing custom function implemented by user.
     198             :  */
     199             : template <typename T, typename UC>
     200             : FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
     201    15234200 : from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
     202             : 
     203             :   static_assert(is_supported_float_type<T>(),
     204             :                 "only some floating-point types are supported");
     205             :   static_assert(is_supported_char_type<UC>(),
     206             :                 "only char, wchar_t, char16_t and char32_t are supported");
     207             : 
     208             :   from_chars_result_t<UC> answer;
     209             : 
     210    15234200 :   answer.ec = std::errc(); // be optimistic
     211    15234200 :   answer.ptr = pns.lastmatch;
     212             :   // The implementation of the Clinger's fast path is convoluted because
     213             :   // we want round-to-nearest in all cases, irrespective of the rounding mode
     214             :   // selected on the thread.
     215             :   // We proceed optimistically, assuming that detail::rounds_to_nearest()
     216             :   // returns true.
     217    15234200 :   if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
     218    30467200 :       pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
     219    15233000 :       !pns.too_many_digits) {
     220             :     // Unfortunately, the conventional Clinger's fast path is only possible
     221             :     // when the system rounds to the nearest float.
     222             :     //
     223             :     // We expect the next branch to almost always be selected.
     224             :     // We could check it first (before the previous branch), but
     225             :     // there might be performance advantages at having the check
     226             :     // be last.
     227    30340900 :     if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
     228             :       // We have that fegetround() == FE_TONEAREST.
     229             :       // Next is Clinger's fast path.
     230    15170400 :       if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
     231    14966700 :         value = T(pns.mantissa);
     232    14966700 :         if (pns.exponent < 0) {
     233    13589200 :           value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
     234             :         } else {
     235     1377570 :           value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
     236             :         }
     237    14966700 :         if (pns.negative) {
     238     6472000 :           value = -value;
     239             :         }
     240    14966700 :         return answer;
     241             :       }
     242             :     } else {
     243             :       // We do not have that fegetround() == FE_TONEAREST.
     244             :       // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's
     245             :       // proposal
     246           0 :       if (pns.exponent >= 0 &&
     247           0 :           pns.mantissa <=
     248           0 :               binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
     249             : #if defined(__clang__) || defined(FASTFLOAT_32BIT)
     250             :         // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
     251             :         if (pns.mantissa == 0) {
     252             :           value = pns.negative ? T(-0.) : T(0.);
     253             :           return answer;
     254             :         }
     255             : #endif
     256           0 :         value = T(pns.mantissa) *
     257           0 :                 binary_format<T>::exact_power_of_ten(pns.exponent);
     258           0 :         if (pns.negative) {
     259           0 :           value = -value;
     260             :         }
     261           0 :         return answer;
     262             :       }
     263             :     }
     264             :   }
     265             :   adjusted_mantissa am =
     266      267453 :       compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
     267      267452 :   if (pns.too_many_digits && am.power2 >= 0) {
     268      125182 :     if (am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
     269           0 :       am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
     270             :     }
     271             :   }
     272             :   // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
     273             :   // and we have an invalid power (am.power2 < 0), then we need to go the long
     274             :   // way around again. This is very uncommon.
     275      267452 :   if (am.power2 < 0) {
     276           0 :     am = digit_comp<T>(pns, am);
     277             :   }
     278      267452 :   to_float(pns.negative, am, value);
     279             :   // Test for over/underflow.
     280      534904 :   if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
     281      267452 :       am.power2 == binary_format<T>::infinite_power()) {
     282           3 :     answer.ec = std::errc::result_out_of_range;
     283             :   }
     284      267452 :   return answer;
     285             : }
     286             : 
     287             : template <typename T, typename UC>
     288             : FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
     289    15276000 : from_chars_advanced(UC const *first, UC const *last, T &value,
     290             :                     parse_options_t<UC> options) noexcept {
     291             : 
     292             :   static_assert(is_supported_float_type<T>(),
     293             :                 "only some floating-point types are supported");
     294             :   static_assert(is_supported_char_type<UC>(),
     295             :                 "only char, wchar_t, char16_t and char32_t are supported");
     296             : 
     297             :   from_chars_result_t<UC> answer;
     298             : #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
     299             :   while ((first != last) && fast_float::is_space(uint8_t(*first))) {
     300             :     first++;
     301             :   }
     302             : #endif
     303    15276000 :   if (first == last) {
     304       41110 :     answer.ec = std::errc::invalid_argument;
     305       41110 :     answer.ptr = first;
     306       41110 :     return answer;
     307             :   }
     308             :   parsed_number_string_t<UC> pns =
     309    15234800 :       parse_number_string<UC>(first, last, options);
     310    15234800 :   if (!pns.valid) {
     311         662 :     if (options.format & chars_format::no_infnan) {
     312           0 :       answer.ec = std::errc::invalid_argument;
     313           0 :       answer.ptr = first;
     314           0 :       return answer;
     315             :     } else {
     316         662 :       return detail::parse_infnan(first, last, value);
     317             :     }
     318             :   }
     319             : 
     320             :   // call overload that takes parsed_number_string_t directly.
     321    15234200 :   return from_chars_advanced(pns, value);
     322             : }
     323             : 
     324             : template <typename T, typename UC, typename>
     325             : FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
     326             : from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
     327             :   static_assert(is_supported_char_type<UC>(),
     328             :                 "only char, wchar_t, char16_t and char32_t are supported");
     329             : 
     330             :   from_chars_result_t<UC> answer;
     331             : #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
     332             :   while ((first != last) && fast_float::is_space(uint8_t(*first))) {
     333             :     first++;
     334             :   }
     335             : #endif
     336             :   if (first == last || base < 2 || base > 36) {
     337             :     answer.ec = std::errc::invalid_argument;
     338             :     answer.ptr = first;
     339             :     return answer;
     340             :   }
     341             :   return parse_int_string(first, last, value, base);
     342             : }
     343             : 
     344             : } // namespace fast_float
     345             : 
     346             : #endif

Generated by: LCOV version 1.14