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: 44 67 65.7 %
Date: 2024-05-14 23:54:21 Functions: 3 3 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             : 
      14             : namespace fast_float {
      15             : 
      16             : 
      17             : namespace detail {
      18             : /**
      19             :  * Special case +inf, -inf, nan, infinity, -infinity.
      20             :  * The case comparisons could be made much faster given that we know that the
      21             :  * strings a null-free and fixed.
      22             :  **/
      23             : template <typename T, typename UC>
      24             : from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
      25         619 : parse_infnan(UC const * first, UC const * last, T &value)  noexcept  {
      26         619 :   from_chars_result_t<UC> answer{};
      27         619 :   answer.ptr = first;
      28         619 :   answer.ec = std::errc(); // be optimistic
      29         619 :   bool minusSign = false;
      30         619 :   if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
      31         238 :       minusSign = true;
      32         238 :       ++first;
      33             :   }
      34             : #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
      35             :   if (*first == UC('+')) {
      36             :       ++first;
      37             :   }
      38             : #endif
      39         619 :   if (last - first >= 3) {
      40           5 :     if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
      41           0 :       answer.ptr = (first += 3);
      42           0 :       value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
      43             :       // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
      44           0 :       if(first != last && *first == UC('(')) {
      45           0 :         for(UC const * ptr = first + 1; ptr != last; ++ptr) {
      46           0 :           if (*ptr == UC(')')) {
      47           0 :             answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
      48           0 :             break;
      49             :           }
      50           0 :           else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
      51           0 :             break; // forbidden char, not nan(n-char-seq-opt)
      52             :         }
      53             :       }
      54           0 :       return answer;
      55             :     }
      56           5 :     if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
      57           0 :       if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
      58           0 :         answer.ptr = first + 8;
      59             :       } else {
      60           0 :         answer.ptr = first + 3;
      61             :       }
      62           0 :       value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
      63           0 :       return answer;
      64             :     }
      65             :   }
      66         619 :   answer.ec = std::errc::invalid_argument;
      67         619 :   return answer;
      68             : }
      69             : 
      70             : /**
      71             :  * Returns true if the floating-pointing rounding mode is to 'nearest'.
      72             :  * It is the default on most system. This function is meant to be inexpensive.
      73             :  * Credit : @mwalcott3
      74             :  */
      75             : fastfloat_really_inline bool rounds_to_nearest() noexcept {
      76             :   // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
      77             : #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
      78             :   return false;
      79             : #endif
      80             :   // See
      81             :   // A fast function to check your floating-point rounding mode
      82             :   // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
      83             :   //
      84             :   // This function is meant to be equivalent to :
      85             :   // prior: #include <cfenv>
      86             :   //  return fegetround() == FE_TONEAREST;
      87             :   // However, it is expected to be much faster than the fegetround()
      88             :   // function call.
      89             :   //
      90             :   // The volatile keywoard prevents the compiler from computing the function
      91             :   // at compile-time.
      92             :   // There might be other ways to prevent compile-time optimizations (e.g., asm).
      93             :   // The value does not need to be std::numeric_limits<float>::min(), any small
      94             :   // value so that 1 + x should round to 1 would do (after accounting for excess
      95             :   // precision, as in 387 instructions).
      96             :   static volatile float fmin = std::numeric_limits<float>::min();
      97     7358940 :   float fmini = fmin; // we copy it so that it gets loaded at most once.
      98             :   //
      99             :   // Explanation:
     100             :   // Only when fegetround() == FE_TONEAREST do we have that
     101             :   // fmin + 1.0f == 1.0f - fmin.
     102             :   //
     103             :   // FE_UPWARD:
     104             :   //  fmin + 1.0f > 1
     105             :   //  1.0f - fmin == 1
     106             :   //
     107             :   // FE_DOWNWARD or  FE_TOWARDZERO:
     108             :   //  fmin + 1.0f == 1
     109             :   //  1.0f - fmin < 1
     110             :   //
     111             :   // Note: This may fail to be accurate if fast-math has been
     112             :   // enabled, as rounding conventions may not apply.
     113             :   #ifdef FASTFLOAT_VISUAL_STUDIO
     114             :   #   pragma warning(push)
     115             :   //  todo: is there a VS warning?
     116             :   //  see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
     117             :   #elif defined(__clang__)
     118             :   #   pragma clang diagnostic push
     119             :   #   pragma clang diagnostic ignored "-Wfloat-equal"
     120             :   #elif defined(__GNUC__)
     121             :   #   pragma GCC diagnostic push
     122             :   #   pragma GCC diagnostic ignored "-Wfloat-equal"
     123             :   #endif
     124     7358940 :   return (fmini + 1.0f == 1.0f - fmini);
     125             :   #ifdef FASTFLOAT_VISUAL_STUDIO
     126             :   #   pragma warning(pop)
     127             :   #elif defined(__clang__)
     128             :   #   pragma clang diagnostic pop
     129             :   #elif defined(__GNUC__)
     130             :   #   pragma GCC diagnostic pop
     131             :   #endif
     132             : }
     133             : 
     134             : } // namespace detail
     135             : 
     136             : template<typename T, typename UC>
     137             : FASTFLOAT_CONSTEXPR20
     138         224 : from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
     139             :                              T &value, chars_format fmt /*= chars_format::general*/)  noexcept  {
     140         224 :   return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
     141             : }
     142             : 
     143             : template<typename T, typename UC>
     144             : FASTFLOAT_CONSTEXPR20
     145     7463760 : from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
     146             :                                       T &value, parse_options_t<UC> options)  noexcept  {
     147             : 
     148             :   static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
     149             :   static_assert (std::is_same<UC, char>::value ||
     150             :                  std::is_same<UC, wchar_t>::value ||
     151             :                  std::is_same<UC, char16_t>::value ||
     152             :                  std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
     153             : 
     154             :   from_chars_result_t<UC> answer;
     155             : #ifdef FASTFLOAT_SKIP_WHITE_SPACE  // disabled by default
     156             :   while ((first != last) && fast_float::is_space(uint8_t(*first))) {
     157             :     first++;
     158             :   }
     159             : #endif
     160     7463760 :   if (first == last) {
     161       40491 :     answer.ec = std::errc::invalid_argument;
     162       40491 :     answer.ptr = first;
     163       40491 :     return answer;
     164             :   }
     165     7423270 :   parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
     166     7423270 :   if (!pns.valid) {
     167         619 :     if (options.format & chars_format::no_infnan) {
     168           0 :       answer.ec = std::errc::invalid_argument;
     169           0 :       answer.ptr = first;
     170           0 :       return answer;
     171             :     } else {
     172         619 :       return detail::parse_infnan(first, last, value);
     173             :     }
     174             :   }
     175             : 
     176     7422650 :   answer.ec = std::errc(); // be optimistic
     177     7422650 :   answer.ptr = pns.lastmatch;
     178             :   // The implementation of the Clinger's fast path is convoluted because
     179             :   // we want round-to-nearest in all cases, irrespective of the rounding mode
     180             :   // selected on the thread.
     181             :   // We proceed optimistically, assuming that detail::rounds_to_nearest() returns
     182             :   // true.
     183     7422650 :   if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
     184             :     // Unfortunately, the conventional Clinger's fast path is only possible
     185             :     // when the system rounds to the nearest float.
     186             :     //
     187             :     // We expect the next branch to almost always be selected.
     188             :     // We could check it first (before the previous branch), but
     189             :     // there might be performance advantages at having the check
     190             :     // be last.
     191    14717900 :     if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest())  {
     192             :       // We have that fegetround() == FE_TONEAREST.
     193             :       // Next is Clinger's fast path.
     194     7358940 :       if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
     195     7156000 :         value = T(pns.mantissa);
     196     7156000 :         if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
     197     1110970 :         else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
     198     7156000 :         if (pns.negative) { value = -value; }
     199     7156000 :         return answer;
     200             :       }
     201             :     } else {
     202             :       // We do not have that fegetround() == FE_TONEAREST.
     203             :       // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's proposal
     204           0 :       if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
     205             : #if defined(__clang__)
     206             :         // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
     207             :         if(pns.mantissa == 0) {
     208             :           value = pns.negative ? -0. : 0.;
     209             :           return answer;
     210             :         }
     211             : #endif
     212           0 :         value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
     213           0 :         if (pns.negative) { value = -value; }
     214           0 :         return answer;
     215             :       }
     216             :     }
     217             :   }
     218      266645 :   adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
     219      266645 :   if(pns.too_many_digits && am.power2 >= 0) {
     220      125032 :     if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
     221           0 :       am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
     222             :     }
     223             :   }
     224             :   // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
     225             :   // then we need to go the long way around again. This is very uncommon.
     226      266645 :   if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
     227      266645 :   to_float(pns.negative, am, value);
     228             :   // Test for over/underflow.
     229      266645 :   if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
     230           3 :     answer.ec = std::errc::result_out_of_range;
     231             :   }
     232      266645 :   return answer;
     233             : }
     234             : 
     235             : } // namespace fast_float
     236             : 
     237             : #endif

Generated by: LCOV version 1.14