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