parse_number.hpp 9.6 KB

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