|
14 | 14 |
|
15 | 15 | #include <__assert>
|
16 | 16 | #include <__config>
|
| 17 | +#include <cctype> |
17 | 18 | #include <charconv>
|
| 19 | +#include <concepts> |
18 | 20 | #include <limits>
|
| 21 | +#include <cstring> |
19 | 22 | #include <type_traits>
|
20 | 23 |
|
21 | 24 | // Included for the _Floating_type_traits class
|
22 | 25 | #include "to_chars_floating_point.h"
|
23 | 26 |
|
24 | 27 | _LIBCPP_BEGIN_NAMESPACE_STD
|
25 | 28 |
|
26 |
| -template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0> |
27 |
| -from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& __value, chars_format __fmt) { |
| 29 | +// Parses an infinity string. |
| 30 | +// Valid strings are case insentitive and contain INF or INFINITY. |
| 31 | +// |
| 32 | +// - __first is the first argument to std::from_chars. When the string is invalid |
| 33 | +// this value is returned as ptr in the result. |
| 34 | +// - __last is the last argument of std::from_chars. |
| 35 | +// - __value is the value argument of std::from_chars, |
| 36 | +// - __ptr is the current position is the input string. This is points beyond |
| 37 | +// the initial I character. |
| 38 | +// - __negative whether a valid string represents -inf or +inf. |
| 39 | +template <floating_point _Tp> |
| 40 | +from_chars_result __from_chars_floating_point_inf( |
| 41 | + const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) { |
| 42 | + if (__last - __ptr < 2) [[unlikely]] |
| 43 | + return {__first, errc::invalid_argument}; |
| 44 | + |
| 45 | + if (std::tolower(__ptr[0]) != 'n' || std::tolower(__ptr[1]) != 'f') [[unlikely]] |
| 46 | + return {__first, errc::invalid_argument}; |
| 47 | + |
| 48 | + __ptr += 2; |
| 49 | + |
| 50 | + // At this point the result is valid and contains INF. |
| 51 | + // When the remaining part contains INITY this will be consumed. Otherwise |
| 52 | + // only INF is consumed. For example INFINITZ will consume INF and ignore |
| 53 | + // INITZ. |
| 54 | + |
| 55 | + if (__last - __ptr >= 5 // |
| 56 | + && std::tolower(__ptr[0]) == 'i' // |
| 57 | + && std::tolower(__ptr[1]) == 'n' // |
| 58 | + && std::tolower(__ptr[2]) == 'i' // |
| 59 | + && std::tolower(__ptr[3]) == 't' // |
| 60 | + && std::tolower(__ptr[4]) == 'y') |
| 61 | + __ptr += 5; |
| 62 | + |
| 63 | + if constexpr (numeric_limits<_Tp>::has_infinity) { |
| 64 | + if (__negative) |
| 65 | + __value = -std::numeric_limits<_Tp>::infinity(); |
| 66 | + else |
| 67 | + __value = std::numeric_limits<_Tp>::infinity(); |
| 68 | + |
| 69 | + return {__ptr, std::errc{}}; |
| 70 | + } else { |
| 71 | + return {__ptr, errc::result_out_of_range}; |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +// Parses an infinita string. |
| 76 | +// Valid strings are case insentitive and contain INF or INFINITY. |
| 77 | +// |
| 78 | +// - __first is the first argument to std::from_chars. When the string is invalid |
| 79 | +// this value is returned as ptr in the result. |
| 80 | +// - __last is the last argument of std::from_chars. |
| 81 | +// - __value is the value argument of std::from_chars, |
| 82 | +// - __ptr is the current position is the input string. This is points beyond |
| 83 | +// the initial N character. |
| 84 | +// - __negative whether a valid string represents -nan or +nan. |
| 85 | +template <floating_point _Tp> |
| 86 | +from_chars_result __from_chars_floating_point_nan( |
| 87 | + const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) { |
| 88 | + if (__last - __ptr < 2) [[unlikely]] |
| 89 | + return {__first, errc::invalid_argument}; |
| 90 | + |
| 91 | + if (std::tolower(__ptr[0]) != 'a' || std::tolower(__ptr[1]) != 'n') [[unlikely]] |
| 92 | + return {__first, errc::invalid_argument}; |
| 93 | + |
| 94 | + __ptr += 2; |
| 95 | + |
| 96 | + // At this point the result is valid and contains NAN. When the remaining |
| 97 | + // part contains ( n-char-sequence_opt ) this will be consumed. Otherwise |
| 98 | + // only NAN is consumed. For example NAN(abcd will consume NAN and ignore |
| 99 | + // (abcd. |
| 100 | + if (__last - __ptr >= 2 && __ptr[0] == '(') { |
| 101 | + size_t __offset = 1; |
| 102 | + do { |
| 103 | + if (__ptr[__offset] == ')') { |
| 104 | + __ptr += __offset + 1; |
| 105 | + break; |
| 106 | + } |
| 107 | + if (__ptr[__offset] != '_' && !std::isalnum(__ptr[__offset])) |
| 108 | + break; |
| 109 | + ++__offset; |
| 110 | + } while (__ptr + __offset != __last); |
| 111 | + } |
| 112 | + |
| 113 | + if (__negative) |
| 114 | + __value = -std::numeric_limits<_Tp>::quiet_NaN(); |
| 115 | + else |
| 116 | + __value = std::numeric_limits<_Tp>::quiet_NaN(); |
| 117 | + |
| 118 | + return {__ptr, std::errc{}}; |
| 119 | +} |
| 120 | + |
| 121 | +template <floating_point _Tp> |
| 122 | +from_chars_result __from_chars_floating_point_decimal( |
| 123 | + const char* const __first, |
| 124 | + const char* __last, |
| 125 | + _Tp& __value, |
| 126 | + chars_format __fmt, |
| 127 | + const char* __ptr, |
| 128 | + bool __negative) { |
28 | 129 | using _Traits = _Floating_type_traits<_Tp>;
|
29 | 130 | using _Uint_type = typename _Traits::_Uint_type;
|
30 | 131 | ptrdiff_t length = __last - __first;
|
31 | 132 | _LIBCPP_ASSERT_INTERNAL(length > 0, "");
|
32 | 133 |
|
33 |
| - // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10 |
34 |
| - // chars_format::general case. Also, no sign, inf, or nan handling. |
35 |
| - _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, ""); |
36 |
| - |
37 |
| - const char* src = __first; // rename to match the libc code copied for this section. |
| 134 | + const char* src = __ptr; // rename to match the libc code copied for this section. |
38 | 135 |
|
39 | 136 | _Uint_type mantissa = 0;
|
40 | 137 | int exponent = 0;
|
@@ -123,10 +220,67 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
|
123 | 220 | auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
|
124 | 221 | result.set_mantissa(expanded_float.mantissa);
|
125 | 222 | result.set_biased_exponent(expanded_float.exponent);
|
126 |
| - __value = result.get_val(); |
| 223 | + if (__negative) |
| 224 | + __value = -result.get_val(); |
| 225 | + else |
| 226 | + __value = result.get_val(); |
127 | 227 | return {src + index, {}};
|
128 | 228 | }
|
129 | 229 |
|
| 230 | +template <floating_point _Tp> |
| 231 | +from_chars_result |
| 232 | +__from_chars_floating_point(const char* const __first, const char* __last, _Tp& __value, chars_format __fmt) { |
| 233 | + if (__first == __last) [[unlikely]] |
| 234 | + return {__first, errc::invalid_argument}; |
| 235 | + |
| 236 | + const char* __ptr = __first; |
| 237 | + |
| 238 | + // skip whitespace |
| 239 | + while (std::isspace(*__ptr)) { |
| 240 | + ++__ptr; |
| 241 | + if (__ptr == __last) [[unlikely]] |
| 242 | + return {__first, errc::invalid_argument}; // is this valid?? |
| 243 | + } |
| 244 | + |
| 245 | + bool __negative = *__ptr == '-'; |
| 246 | + if (__negative) { |
| 247 | + ++__ptr; |
| 248 | + if (__ptr == __last) [[unlikely]] |
| 249 | + return {__first, errc::invalid_argument}; |
| 250 | + } |
| 251 | + |
| 252 | + if (!std::isdigit(*__ptr)) { |
| 253 | + // TODO Evaluate the other implementations |
| 254 | + // [charconv.from.chars]/6.2 |
| 255 | + // if fmt has chars_format::scientific set but not chars_format::fixed, |
| 256 | + // the otherwise optional exponent part shall appear; |
| 257 | + // Since INF/NAN do not have an exponent this value is not valid. |
| 258 | + // See LWG3456 |
| 259 | + if (__fmt == chars_format::scientific) |
| 260 | + return {__first, errc::invalid_argument}; |
| 261 | + |
| 262 | + switch (std::tolower(*__ptr)) { |
| 263 | + case 'i': |
| 264 | + return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative); |
| 265 | + case 'n': |
| 266 | + if constexpr (numeric_limits<_Tp>::has_quiet_NaN) |
| 267 | + return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative); |
| 268 | + [[fallthrough]]; |
| 269 | + default: |
| 270 | + return {__first, errc::invalid_argument}; |
| 271 | + } |
| 272 | + } |
| 273 | + |
| 274 | +#if 1 |
| 275 | + _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, ""); |
| 276 | +#else |
| 277 | + if (__fmt == chars_format::hex) |
| 278 | + return std::__from_chars_floating_point_hex(__first, __last, __value); |
| 279 | +#endif |
| 280 | + |
| 281 | + return std::__from_chars_floating_point_decimal(__first, __last, __value, __fmt, __ptr, __negative); |
| 282 | +} |
| 283 | + |
130 | 284 | _LIBCPP_END_NAMESPACE_STD
|
131 | 285 |
|
132 | 286 | #endif //_LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
|
0 commit comments