LCOV - code coverage report
Current view: top level - json/detail/charconv/detail/fast_float - parse_number.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 42.9 % 63 27 36
Test Date: 2026-03-06 10:50:33 Functions: 66.7 % 3 2 1

           TLA  Line data    Source code
       1                 : // Copyright 2020-2023 Daniel Lemire
       2                 : // Copyright 2023 Matt Borland
       3                 : // Distributed under the Boost Software License, Version 1.0.
       4                 : // https://www.boost.org/LICENSE_1_0.txt
       5                 : //
       6                 : // Derivative of: https://github.com/fastfloat/fast_float
       7                 : 
       8                 : #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
       9                 : #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
      10                 : 
      11                 : #include <boost/json/detail/charconv/detail/fast_float/ascii_number.hpp>
      12                 : #include <boost/json/detail/charconv/detail/fast_float/decimal_to_binary.hpp>
      13                 : #include <boost/json/detail/charconv/detail/fast_float/digit_comparison.hpp>
      14                 : #include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
      15                 : 
      16                 : #include <cmath>
      17                 : #include <cstring>
      18                 : #include <limits>
      19                 : #include <system_error>
      20                 : 
      21                 : namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
      22                 : 
      23                 : 
      24                 : namespace detail {
      25                 : /**
      26                 :  * Special case +inf, -inf, nan, infinity, -infinity.
      27                 :  * The case comparisons could be made much faster given that we know that the
      28                 :  * strings a null-free and fixed.
      29                 :  **/
      30                 : 
      31                 : #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
      32                 : # pragma GCC diagnostic push
      33                 : # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
      34                 : #endif
      35                 : 
      36                 : template <typename T, typename UC>
      37                 : from_chars_result_t<UC> BOOST_JSON_CXX14_CONSTEXPR
      38 MIS           0 : parse_infnan(UC const * first, UC const * last, T &value)  noexcept  {
      39               0 :   from_chars_result_t<UC> answer{};
      40               0 :   answer.ptr = first;
      41               0 :   answer.ec = std::errc(); // be optimistic
      42               0 :   bool minusSign = false;
      43               0 :   if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
      44               0 :       minusSign = true;
      45               0 :       ++first;
      46                 :   }
      47               0 :   if (last - first >= 3) {
      48               0 :     if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
      49               0 :       answer.ptr = (first += 3);
      50               0 :       value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
      51                 :       // 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).
      52               0 :       if(first != last && *first == UC('(')) {
      53               0 :         for(UC const * ptr = first + 1; ptr != last; ++ptr) {
      54               0 :           if (*ptr == UC(')')) {
      55               0 :             answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
      56               0 :             break;
      57                 :           }
      58               0 :           else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
      59               0 :             break; // forbidden char, not nan(n-char-seq-opt)
      60                 :         }
      61                 :       }
      62               0 :       return answer;
      63                 :     }
      64               0 :     if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
      65               0 :       if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
      66               0 :         answer.ptr = first + 8;
      67                 :       } else {
      68               0 :         answer.ptr = first + 3;
      69                 :       }
      70               0 :       value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
      71               0 :       return answer;
      72                 :     }
      73                 :   }
      74               0 :   answer.ec = std::errc::invalid_argument;
      75               0 :   return answer;
      76                 : }
      77                 : 
      78                 : #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
      79                 : # pragma GCC diagnostic pop
      80                 : #endif
      81                 : 
      82                 : /**
      83                 :  * Returns true if the floating-pointing rounding mode is to 'nearest'.
      84                 :  * It is the default on most system. This function is meant to be inexpensive.
      85                 :  * Credit : @mwalcott3
      86                 :  */
      87                 : BOOST_FORCEINLINE bool rounds_to_nearest() noexcept {
      88                 :   // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
      89                 : #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
      90                 :   return false;
      91                 : #endif
      92                 :   // See
      93                 :   // A fast function to check your floating-point rounding mode
      94                 :   // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
      95                 :   //
      96                 :   // This function is meant to be equivalent to :
      97                 :   // prior: #include <cfenv>
      98                 :   //  return fegetround() == FE_TONEAREST;
      99                 :   // However, it is expected to be much faster than the fegetround()
     100                 :   // function call.
     101                 :   //
     102                 :   // The volatile keywoard prevents the compiler from computing the function
     103                 :   // at compile-time.
     104                 :   // There might be other ways to prevent compile-time optimizations (e.g., asm).
     105                 :   // The value does not need to be std::numeric_limits<float>::min(), any small
     106                 :   // value so that 1 + x should round to 1 would do (after accounting for excess
     107                 :   // precision, as in 387 instructions).
     108                 :   static volatile float fmin = (std::numeric_limits<float>::min)();
     109 HIT        5895 :   float fmini = fmin; // we copy it so that it gets loaded at most once.
     110                 :   //
     111                 :   // Explanation:
     112                 :   // Only when fegetround() == FE_TONEAREST do we have that
     113                 :   // fmin + 1.0f == 1.0f - fmin.
     114                 :   //
     115                 :   // FE_UPWARD:
     116                 :   //  fmin + 1.0f > 1
     117                 :   //  1.0f - fmin == 1
     118                 :   //
     119                 :   // FE_DOWNWARD or  FE_TOWARDZERO:
     120                 :   //  fmin + 1.0f == 1
     121                 :   //  1.0f - fmin < 1
     122                 :   //
     123                 :   // Note: This may fail to be accurate if fast-math has been
     124                 :   // enabled, as rounding conventions may not apply.
     125                 :   #ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
     126                 :   #   pragma warning(push)
     127                 :   //  todo: is there a VS warning?
     128                 :   //  see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
     129                 :   #elif defined(__clang__)
     130                 :   #   pragma clang diagnostic push
     131                 :   #   pragma clang diagnostic ignored "-Wfloat-equal"
     132                 :   #elif defined(__GNUC__)
     133                 :   #   pragma GCC diagnostic push
     134                 :   #   pragma GCC diagnostic ignored "-Wfloat-equal"
     135                 :   #endif
     136            5895 :   return (fmini + 1.0f == 1.0f - fmini);
     137                 :   #ifdef BOOST_JSON_FASTFLOAT_VISUAL_STUDIO
     138                 :   #   pragma warning(pop)
     139                 :   #elif defined(__clang__)
     140                 :   #   pragma clang diagnostic pop
     141                 :   #elif defined(__GNUC__)
     142                 :   #   pragma GCC diagnostic pop
     143                 :   #endif
     144                 : }
     145                 : 
     146                 : } // namespace detail
     147                 : 
     148                 : template<typename T, typename UC>
     149                 : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     150         1009310 : from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
     151                 :                              T &value, chars_format fmt /*= chars_format::general*/)  noexcept  {
     152         1009310 :   return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
     153                 : }
     154                 : 
     155                 : template<typename T, typename UC>
     156                 : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     157         1009310 : from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
     158                 :                                       T &value, parse_options_t<UC> options)  noexcept  {
     159                 : 
     160                 :   static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
     161                 :   static_assert (std::is_same<UC, char>::value ||
     162                 :                  std::is_same<UC, wchar_t>::value ||
     163                 :                  std::is_same<UC, char16_t>::value ||
     164                 :                  std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
     165                 : 
     166                 :   from_chars_result_t<UC> answer;
     167         1009310 :   if (first == last) {
     168 MIS           0 :     answer.ec = std::errc::invalid_argument;
     169               0 :     answer.ptr = first;
     170               0 :     return answer;
     171                 :   }
     172 HIT     1009310 :   parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
     173         1009310 :   if (!pns.valid) {
     174 MIS           0 :     return detail::parse_infnan(first, last, value);
     175                 :   }
     176 HIT     1009310 :   answer.ec = std::errc(); // be optimistic
     177         1009310 :   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         1009310 :   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           11790 :     if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest())  {
     192                 :       // We have that fegetround() == FE_TONEAREST.
     193                 :       // Next is Clinger's fast path.
     194            5895 :       if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
     195            5570 :         value = T(pns.mantissa);
     196            5570 :         if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
     197            1756 :         else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
     198            5570 :         if (pns.negative) { value = -value; }
     199            5570 :         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 MIS           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 HIT     1003740 :   adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
     219         1003740 :   if(pns.too_many_digits && am.power2 >= 0) {
     220         2001424 :     if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
     221            5972 :       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         1003740 :   if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
     227         1003740 :   to_float(pns.negative, am, value);
     228                 :   // Test for over/underflow.
     229         1003740 :   if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
     230           30608 :     answer.ec = std::errc::result_out_of_range;
     231                 :   }
     232         1003740 :   return answer;
     233                 : }
     234                 : 
     235                 : }}}}}} // namespace fast_float
     236                 : 
     237                 : #endif
        

Generated by: LCOV version 2.3