23 #ifndef GUL14_TO_NUMBER_H_
24 #define GUL14_TO_NUMBER_H_
32 #include <type_traits>
50 constexpr
inline bool is_digit(
char c) noexcept
52 return c >=
'0' && c <=
'9';
55 constexpr
inline bool is_nan_specifier(
char c) noexcept
57 if (c >=
'0' && c <=
'9')
59 if (c >=
'a' && c <=
'z')
61 if (c >=
'A' && c <=
'Z')
68 template <
typename NumberType,
bool count_magnitude = false>
70 NumberType* magnitude =
nullptr) noexcept
73 constexpr NumberType max_tenth = std::numeric_limits<NumberType>::max() / 10;
79 NumberType result = 0;
87 if (__builtin_mul_overflow(result, NumberType{ 10 }, &result))
90 if (__builtin_add_overflow(result,
static_cast<NumberType
>(c -
'0'), &result))
93 if (result > max_tenth)
105 *magnitude *= NumberType{ 10 };
116 constexpr optional<int> parse_exponent(
string_view str) noexcept
118 bool negative =
false;
123 str.remove_prefix(1);
126 str.remove_prefix(1);
133 auto opt_exp = to_unsigned_integer<int>(str);
149 template <
typename NumberType>
150 using FloatConversionIntType =
151 typename std::conditional<
152 (std::numeric_limits<std::uint64_t>::digits10 >= std::numeric_limits<NumberType>::digits10),
154 #ifdef __SIZEOF_INT128__
177 template <
typename NumberType>
180 static_assert(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10
181 >= std::numeric_limits<NumberType>::digits10,
182 "FloatConversionIntType is too small for NumberType");
184 i1 = i1.substr(0, std::min(i1.length(),
185 size_t(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10)));
186 i2 = i2.substr(0, std::min(i2.length(),
187 size_t(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10) - i1.length()));
189 FloatConversionIntType<NumberType> accu{ 0 };
191 auto magnitude = FloatConversionIntType<NumberType>{ 1 };
193 if (not i2.empty()) {
194 auto f2 = to_unsigned_integer<FloatConversionIntType<NumberType>,
true>(i2, &magnitude);
195 if (not f2.has_value())
199 if (not i1.empty()) {
200 auto i2_magnitude = magnitude;
201 auto f1 = to_unsigned_integer<FloatConversionIntType<NumberType>,
true>(i1, &magnitude);
202 if (not f1.has_value())
204 accu += (*f1 * i2_magnitude);
207 return static_cast<NumberType
>(accu) /
static_cast<NumberType
>(magnitude / 10);
210 template <
typename NumberType>
211 struct ParseInfNanResult {
213 optional<NumberType> result;
235 template <
typename NumberType>
236 constexpr
inline ParseInfNanResult<NumberType> parse_inf_nan(
gul14::string_view str) noexcept
238 auto const strlength = str.length();
244 return {
true, make_optional(std::numeric_limits<NumberType>::infinity()) };
247 return {
true, make_optional(std::numeric_limits<NumberType>::infinity()) };
253 return {
true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
254 if (strlength < 5 or str[3] !=
'(' or str.back() !=
')')
256 str.remove_prefix(4);
257 str.remove_suffix(1);
258 while (str.length()) {
259 if (not is_nan_specifier(str.front()))
261 str.remove_prefix(1);
264 return {
true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
266 return {
false, {} };
281 long double pow10(
int exponent);
300 template <
typename NumberType>
301 constexpr
inline optional<NumberType> to_unsigned_float(
gul14::string_view str) noexcept
303 auto inf_nan = parse_inf_nan<NumberType>(str);
304 if (inf_nan.result_valid)
305 return inf_nan.result;
308 auto e_pos = str.find_first_of(
"eE");
309 if (e_pos != gul14::string_view::npos)
311 if (e_pos + 1 == str.size())
314 auto str_exponent = str.substr(e_pos + 1);
316 str = str.substr(0, e_pos);
318 auto opt_exp = detail::parse_exponent(str_exponent);
329 auto point_pos = str.find(
'.');
330 if (point_pos != gul14::string_view::npos)
332 str_before_point = str.substr(0, point_pos);
333 str_after_point = str.substr(point_pos + 1);
336 if (str_before_point.empty() && str_after_point.empty())
340 while (!str_before_point.empty() and str_before_point[0] ==
'0')
341 str_before_point.remove_prefix(1);
344 if (str_before_point.empty()) {
345 auto const old_digits = str_after_point.length();
346 while (!str_after_point.empty() and str_after_point[0] ==
'0')
347 str_after_point.remove_prefix(1);
349 if (str_after_point.empty())
352 str_before_point = str_after_point.substr(0, 1);
353 str_after_point.remove_prefix(1);
354 exponent -=
static_cast<int>(old_digits - str_after_point.length());
356 exponent +=
static_cast<int>(str_before_point.length() - 1);
364 using long_double =
long double;
365 using CalcType = std::conditional_t<
366 std::greater<>()(
sizeof(NumberType),
sizeof(
double)),
367 long_double,
double>;
369 auto norm_val = to_normalized_float<CalcType>(str_before_point, str_after_point);
370 if (not norm_val.has_value())
373 return detail::pow10(exponent) * *norm_val;
393 template <
typename NumberType>
401 auto input = std::string{ str };
403 auto value =
static_cast<NumberType
>(std::strtold(input.c_str(), &process_end));
405 if (input.data() + input.size() != process_end)
409 catch (
const std::exception &)
496 template <
typename NumberType>
497 constexpr
inline std::enable_if_t<std::is_integral<NumberType>::value and
498 std::is_unsigned<NumberType>::value,
499 optional<NumberType>>
502 return detail::to_unsigned_integer<NumberType>(str);
506 template <
typename NumberType>
507 constexpr
inline std::enable_if_t<std::is_integral<NumberType>::value and
508 std::is_signed<NumberType>::value,
509 optional<NumberType>>
515 if (str.front() ==
'-')
517 using UnsignedT = std::make_unsigned_t<NumberType>;
518 constexpr
auto max_abs_negative_value =
519 static_cast<UnsignedT
>(std::numeric_limits<NumberType>::max()) + 1;
521 str.remove_prefix(1);
523 auto result = detail::to_unsigned_integer<UnsignedT>(str);
527 if (*result == max_abs_negative_value)
528 return std::numeric_limits<NumberType>::lowest();
529 else if (*result > max_abs_negative_value)
532 return static_cast<NumberType
>(-
static_cast<NumberType
>(*result));
535 return detail::to_unsigned_integer<NumberType>(str);
539 template <
typename NumberType>
540 constexpr
inline std::enable_if_t<std::is_floating_point<NumberType>::value,
541 optional<NumberType>>
549 # pragma warning( push )
550 # pragma warning( disable: 4127 )
552 (std::numeric_limits<detail::FloatConversionIntType<NumberType>>::digits10
553 <= std::numeric_limits<NumberType>::digits10)) {
555 # pragma warning( pop )
560 return detail::strtold_wrapper<NumberType>(str);
563 if (str.front() ==
'-')
565 str.remove_prefix(1);
566 auto result = detail::to_unsigned_float<NumberType>(str);
572 return detail::to_unsigned_float<NumberType>(str);
579 if (str.length() == 1) {
A class template that can either contain a value of a certain type or not.
Definition: optional.h:274
basic_string_view< char > string_view
A view to a contiguous sequence of chars.
Definition: string_view.h:624
constexpr bool equals_nocase(string_view str1, string_view str2) noexcept
Determine whether a string is equal to another one, making no distinction between upper and lower cas...
Definition: substring_checks.h:165
constexpr bool starts_with_nocase(string_view str, string_view prefix) noexcept
Determine whether a string starts with another string.
Definition: substring_checks.h:313
constexpr std::enable_if_t< std::is_integral< NumberType >::value and std::is_unsigned< NumberType >::value, optional< NumberType > > to_number(gul14::string_view str) noexcept
Convert an ASCII string_view into a number.
Definition: to_number.h:500
Definition of macros used internally by GUL.
Namespace gul14 contains all functions and classes of the General Utility Library.
Definition: doxygen.h:26
Implementation of gul14::optional.
Provides a gul14::string_view that is fully compatible with C++17's std::string_view.
Definition of contains(), ends_with(), and starts_with().