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

source code of boost/libs/charconv/include/boost/charconv/detail/fast_float/parse_number.hpp