#ifndef FASTFLOAT_PARSE_NUMBER_H #define FASTFLOAT_PARSE_NUMBER_H #include "ascii_number.h" #include "decimal_to_binary.h" #include "digit_comparison.h" #include "float_common.h" #include #include #include #include namespace fast_float { namespace detail { /** * Special case +inf, -inf, nan, infinity, -infinity. * The case comparisons could be made much faster given that we know that the * strings a null-free and fixed. **/ template from_chars_result_t FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, T &value, chars_format fmt) noexcept { from_chars_result_t answer{}; answer.ptr = first; answer.ec = std::errc(); // be optimistic // assume first < last, so dereference without checks; bool const minusSign = (*first == UC('-')); // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if ((*first == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && (*first == UC('+')))) { ++first; } if (last - first >= 3) { if (fastfloat_strncasecmp(first, str_const_nan(), 3)) { answer.ptr = (first += 3); value = minusSign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); // 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). if (first != last && *first == UC('(')) { for (UC const *ptr = first + 1; ptr != last; ++ptr) { if (*ptr == UC(')')) { answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) break; } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_'))) break; // forbidden char, not nan(n-char-seq-opt) } } return answer; } if (fastfloat_strncasecmp(first, str_const_inf(), 3)) { if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf() + 3, 5)) { answer.ptr = first + 8; } else { answer.ptr = first + 3; } value = minusSign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); return answer; } } answer.ec = std::errc::invalid_argument; return answer; } /** * Returns true if the floating-pointing rounding mode is to 'nearest'. * It is the default on most system. This function is meant to be inexpensive. * Credit : @mwalcott3 */ fastfloat_really_inline bool rounds_to_nearest() noexcept { // https://lemire.me/blog/2020/06/26/gcc-not-nearest/ #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return false; #endif // See // A fast function to check your floating-point rounding mode // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ // // This function is meant to be equivalent to : // prior: #include // return fegetround() == FE_TONEAREST; // However, it is expected to be much faster than the fegetround() // function call. // // The volatile keyword prevents the compiler from computing the function // at compile-time. // There might be other ways to prevent compile-time optimizations (e.g., // asm). The value does not need to be std::numeric_limits::min(), any // small value so that 1 + x should round to 1 would do (after accounting for // excess precision, as in 387 instructions). static float volatile fmin = std::numeric_limits::min(); float fmini = fmin; // we copy it so that it gets loaded at most once. // // Explanation: // Only when fegetround() == FE_TONEAREST do we have that // fmin + 1.0f == 1.0f - fmin. // // FE_UPWARD: // fmin + 1.0f > 1 // 1.0f - fmin == 1 // // FE_DOWNWARD or FE_TOWARDZERO: // fmin + 1.0f == 1 // 1.0f - fmin < 1 // // Note: This may fail to be accurate if fast-math has been // enabled, as rounding conventions may not apply. #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) // todo: is there a VS warning? // see // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 #elif defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif return (fmini + 1.0f == 1.0f - fmini); #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(pop) #elif defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif } } // namespace detail template struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_advanced(first, last, value, options); } }; #ifdef __STDCPP_FLOAT32_T__ template <> struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, std::float32_t &value, parse_options_t options) noexcept { // if std::float32_t is defined, and we are in C++23 mode; macro set for // float32; set value to float due to equivalence between float and // float32_t float val; auto ret = from_chars_advanced(first, last, val, options); value = val; return ret; } }; #endif #ifdef __STDCPP_FLOAT64_T__ template <> struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, std::float64_t &value, parse_options_t options) noexcept { // if std::float64_t is defined, and we are in C++23 mode; macro set for // float64; set value as double due to equivalence between double and // float64_t double val; auto ret = from_chars_advanced(first, last, val, options); value = val; return ret; } }; #endif template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, chars_format fmt /*= chars_format::general*/) noexcept { return from_chars_caller::call(first, last, value, parse_options_t(fmt)); } /** * This function overload takes parsed_number_string_t structure that is created * and populated either by from_chars_advanced function taking chars range and * parsing options or other parsing custom function implemented by user. */ template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { static_assert(is_supported_float_type::value, "only some floating-point types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); from_chars_result_t answer; answer.ec = std::errc(); // be optimistic answer.ptr = pns.lastmatch; // The implementation of the Clinger's fast path is convoluted because // we want round-to-nearest in all cases, irrespective of the rounding mode // selected on the thread. // We proceed optimistically, assuming that detail::rounds_to_nearest() // returns true. if (binary_format::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && !pns.too_many_digits) { // Unfortunately, the conventional Clinger's fast path is only possible // when the system rounds to the nearest float. // // We expect the next branch to almost always be selected. // We could check it first (before the previous branch), but // there might be performance advantages at having the check // be last. if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { // We have that fegetround() == FE_TONEAREST. // Next is Clinger's fast path. if (pns.mantissa <= binary_format::max_mantissa_fast_path()) { value = T(pns.mantissa); if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } else { value = value * binary_format::exact_power_of_ten(pns.exponent); } if (pns.negative) { value = -value; } return answer; } } else { // We do not have that fegetround() == FE_TONEAREST. // Next is a modified Clinger's fast path, inspired by Jakub JelĂ­nek's // proposal if (pns.exponent >= 0 && pns.mantissa <= binary_format::max_mantissa_fast_path(pns.exponent)) { #if defined(__clang__) || defined(FASTFLOAT_32BIT) // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD if (pns.mantissa == 0) { value = pns.negative ? T(-0.) : T(0.); return answer; } #endif value = T(pns.mantissa) * binary_format::exact_power_of_ten(pns.exponent); if (pns.negative) { value = -value; } return answer; } } } adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); if (pns.too_many_digits && am.power2 >= 0) { if (am != compute_float>(pns.exponent, pns.mantissa + 1)) { am = compute_error>(pns.exponent, pns.mantissa); } } // If we called compute_float>(pns.exponent, pns.mantissa) // and we have an invalid power (am.power2 < 0), then we need to go the long // way around again. This is very uncommon. if (am.power2 < 0) { am = digit_comp(pns, am); } to_float(pns.negative, am, value); // Test for over/underflow. if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format::infinite_power()) { answer.ec = std::errc::result_out_of_range; } return answer; } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_float_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { static_assert(is_supported_float_type::value, "only some floating-point types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); chars_format const fmt = detail::adjust_for_feature_macros(options.format); from_chars_result_t answer; if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } if (first == last) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } parsed_number_string_t pns = parse_number_string(first, last, options); if (!pns.valid) { if (uint64_t(fmt & chars_format::no_infnan)) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } else { return detail::parse_infnan(first, last, value, fmt); } } // call overload that takes parsed_number_string_t directly. return from_chars_advanced(pns, value); } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, int base) noexcept { static_assert(is_supported_integer_type::value, "only integer types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); parse_options_t options; options.base = base; return from_chars_advanced(first, last, value, options); } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_int_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { static_assert(is_supported_integer_type::value, "only integer types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); chars_format const fmt = detail::adjust_for_feature_macros(options.format); int const base = options.base; from_chars_result_t answer; if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } if (first == last || base < 2 || base > 36) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } return parse_int_string(first, last, value, options); } template struct from_chars_advanced_caller { static_assert(TypeIx > 0, "unsupported type"); }; template <> struct from_chars_advanced_caller<1> { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_float_advanced(first, last, value, options); } }; template <> struct from_chars_advanced_caller<2> { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_int_advanced(first, last, value, options); } }; template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_advanced_caller< size_t(is_supported_float_type::value) + 2 * size_t(is_supported_integer_type::value)>::call(first, last, value, options); } } // namespace fast_float #endif