Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-19 08:19:38

0001 // Copyright 2020-2023 Daniel Lemire
0002 // Copyright 2023 Matt Borland
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // https://www.boost.org/LICENSE_1_0.txt
0005 //
0006 // Derivative of: https://github.com/fastfloat/fast_float
0007 
0008 #ifndef BOOST_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
0009 #define BOOST_CHARCONV_DETAIL_FASTFLOAT_PARSE_NUMBER_HPP
0010 
0011 #include <boost/charconv/detail/fast_float/ascii_number.hpp>
0012 #include <boost/charconv/detail/fast_float/decimal_to_binary.hpp>
0013 #include <boost/charconv/detail/fast_float/digit_comparison.hpp>
0014 #include <boost/charconv/detail/fast_float/float_common.hpp>
0015 
0016 #include <cmath>
0017 #include <cstring>
0018 #include <limits>
0019 #include <system_error>
0020 
0021 namespace boost { namespace charconv { namespace detail { namespace fast_float {
0022 
0023 
0024 namespace detail {
0025 /**
0026  * Special case +inf, -inf, nan, infinity, -infinity.
0027  * The case comparisons could be made much faster given that we know that the
0028  * strings a null-free and fixed.
0029  **/
0030 
0031 #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
0032 # pragma GCC diagnostic push
0033 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
0034 #endif
0035 
0036 template <typename T, typename UC>
0037 from_chars_result_t<UC> BOOST_CHARCONV_FASTFLOAT_CONSTEXPR14
0038 parse_infnan(UC const * first, UC const * last, T &value)  noexcept  {
0039   from_chars_result_t<UC> answer{};
0040   answer.ptr = first;
0041   answer.ec = std::errc(); // be optimistic
0042   bool minusSign = false;
0043   if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
0044       minusSign = true;
0045       ++first;
0046   }
0047 #ifdef BOOST_CHARCONV_FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
0048   if (*first == UC('+')) {
0049       ++first;
0050   }
0051 #endif
0052   if (last - first >= 3) {
0053     if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
0054       answer.ptr = (first += 3);
0055       value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
0056       // 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).
0057       if(first != last && *first == UC('(')) {
0058         for(UC const * ptr = first + 1; ptr != last; ++ptr) {
0059           if (*ptr == UC(')')) {
0060             answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
0061             break;
0062           }
0063           else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
0064             break; // forbidden char, not nan(n-char-seq-opt)
0065         }
0066       }
0067       return answer;
0068     }
0069     if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
0070       if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
0071         answer.ptr = first + 8;
0072       } else {
0073         answer.ptr = first + 3;
0074       }
0075       value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
0076       return answer;
0077     }
0078   }
0079   answer.ec = std::errc::invalid_argument;
0080   return answer;
0081 }
0082 
0083 #if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__)
0084 # pragma GCC diagnostic pop
0085 #endif
0086 
0087 /**
0088  * Returns true if the floating-pointing rounding mode is to 'nearest'.
0089  * It is the default on most system. This function is meant to be inexpensive.
0090  * Credit : @mwalcott3
0091  */
0092 BOOST_FORCEINLINE bool rounds_to_nearest() noexcept {
0093   // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
0094 #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
0095   return false;
0096 #endif
0097   // See
0098   // A fast function to check your floating-point rounding mode
0099   // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
0100   //
0101   // This function is meant to be equivalent to :
0102   // prior: #include <cfenv>
0103   //  return fegetround() == FE_TONEAREST;
0104   // However, it is expected to be much faster than the fegetround()
0105   // function call.
0106   //
0107   // The volatile keywoard prevents the compiler from computing the function
0108   // at compile-time.
0109   // There might be other ways to prevent compile-time optimizations (e.g., asm).
0110   // The value does not need to be std::numeric_limits<float>::min(), any small
0111   // value so that 1 + x should round to 1 would do (after accounting for excess
0112   // precision, as in 387 instructions).
0113   static volatile float fmin = std::numeric_limits<float>::min();
0114   float fmini = fmin; // we copy it so that it gets loaded at most once.
0115   //
0116   // Explanation:
0117   // Only when fegetround() == FE_TONEAREST do we have that
0118   // fmin + 1.0f == 1.0f - fmin.
0119   //
0120   // FE_UPWARD:
0121   //  fmin + 1.0f > 1
0122   //  1.0f - fmin == 1
0123   //
0124   // FE_DOWNWARD or  FE_TOWARDZERO:
0125   //  fmin + 1.0f == 1
0126   //  1.0f - fmin < 1
0127   //
0128   // Note: This may fail to be accurate if fast-math has been
0129   // enabled, as rounding conventions may not apply.
0130   #ifdef BOOST_CHARCONV_FASTFLOAT_VISUAL_STUDIO
0131   #   pragma warning(push)
0132   //  todo: is there a VS warning?
0133   //  see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
0134   #elif defined(__clang__)
0135   #   pragma clang diagnostic push
0136   #   pragma clang diagnostic ignored "-Wfloat-equal"
0137   #elif defined(__GNUC__)
0138   #   pragma GCC diagnostic push
0139   #   pragma GCC diagnostic ignored "-Wfloat-equal"
0140   #endif
0141   return (fmini + 1.0f == 1.0f - fmini);
0142   #ifdef BOOST_CHARCONV_FASTFLOAT_VISUAL_STUDIO
0143   #   pragma warning(pop)
0144   #elif defined(__clang__)
0145   #   pragma clang diagnostic pop
0146   #elif defined(__GNUC__)
0147   #   pragma GCC diagnostic pop
0148   #endif
0149 }
0150 
0151 } // namespace detail
0152 
0153 template<typename T, typename UC>
0154 BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0155 from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
0156                              T &value, chars_format fmt /*= chars_format::general*/)  noexcept  {
0157   return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
0158 }
0159 
0160 template<typename T, typename UC>
0161 BOOST_CHARCONV_FASTFLOAT_CONSTEXPR20
0162 from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
0163                                       T &value, parse_options_t<UC> options)  noexcept  {
0164 
0165   static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
0166   static_assert (std::is_same<UC, char>::value ||
0167                  std::is_same<UC, wchar_t>::value ||
0168                  std::is_same<UC, char16_t>::value ||
0169                  std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
0170 
0171   from_chars_result_t<UC> answer;
0172 #ifdef BOOST_CHARCONV_FASTFLOAT_SKIP_WHITE_SPACE  // disabled by default
0173   while ((first != last) && fast_float::is_space(uint8_t(*first))) {
0174     first++;
0175   }
0176 #endif
0177   if (first == last) {
0178     answer.ec = std::errc::invalid_argument;
0179     answer.ptr = first;
0180     return answer;
0181   }
0182   parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
0183   if (!pns.valid) {
0184     return detail::parse_infnan(first, last, value);
0185   }
0186   answer.ec = std::errc(); // be optimistic
0187   answer.ptr = pns.lastmatch;
0188   // The implementation of the Clinger's fast path is convoluted because
0189   // we want round-to-nearest in all cases, irrespective of the rounding mode
0190   // selected on the thread.
0191   // We proceed optimistically, assuming that detail::rounds_to_nearest() returns
0192   // true.
0193   if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
0194     // Unfortunately, the conventional Clinger's fast path is only possible
0195     // when the system rounds to the nearest float.
0196     //
0197     // We expect the next branch to almost always be selected.
0198     // We could check it first (before the previous branch), but
0199     // there might be performance advantages at having the check
0200     // be last.
0201     if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest())  {
0202       // We have that fegetround() == FE_TONEAREST.
0203       // Next is Clinger's fast path.
0204       if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
0205         value = T(pns.mantissa);
0206         if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
0207         else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
0208         if (pns.negative) { value = -value; }
0209         return answer;
0210       }
0211     } else {
0212       // We do not have that fegetround() == FE_TONEAREST.
0213       // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's proposal
0214       if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
0215 #if defined(__clang__)
0216         // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
0217         if(pns.mantissa == 0) {
0218           value = pns.negative ? -0. : 0.;
0219           return answer;
0220         }
0221 #endif
0222         value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
0223         if (pns.negative) { value = -value; }
0224         return answer;
0225       }
0226     }
0227   }
0228   adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
0229   if(pns.too_many_digits && am.power2 >= 0) {
0230     if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
0231       am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa);
0232     }
0233   }
0234   // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0),
0235   // then we need to go the long way around again. This is very uncommon.
0236   if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
0237   to_float(pns.negative, am, value);
0238   // Test for over/underflow.
0239   if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
0240     answer.ec = std::errc::result_out_of_range;
0241   }
0242   return answer;
0243 }
0244 
0245 }}}} // namespace fast_float
0246 
0247 #endif