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 &)
490 template <
typename NumberType,
491 std::enable_if_t<std::is_integral<NumberType>::value &&
492 std::is_unsigned<NumberType>::value,
int> = 0>
495 return detail::to_unsigned_integer<NumberType>(str);
499 template <
typename NumberType,
500 std::enable_if_t<std::is_integral<NumberType>::value &&
501 std::is_signed<NumberType>::value,
int> = 0>
507 if (str.front() ==
'-')
509 using UnsignedT = std::make_unsigned_t<NumberType>;
510 constexpr
auto max_abs_negative_value =
511 static_cast<UnsignedT
>(std::numeric_limits<NumberType>::max()) + 1;
513 str.remove_prefix(1);
515 auto result = detail::to_unsigned_integer<UnsignedT>(str);
519 if (*result == max_abs_negative_value)
520 return std::numeric_limits<NumberType>::lowest();
521 else if (*result > max_abs_negative_value)
524 return static_cast<NumberType
>(-
static_cast<NumberType
>(*result));
527 return detail::to_unsigned_integer<NumberType>(str);
531 template <
typename NumberType,
532 std::enable_if_t<std::is_floating_point<NumberType>::value,
int> = 0>
540 # pragma warning( push )
541 # pragma warning( disable: 4127 )
543 (std::numeric_limits<detail::FloatConversionIntType<NumberType>>::digits10
544 <= std::numeric_limits<NumberType>::digits10)) {
546 # pragma warning( pop )
551 return detail::strtold_wrapper<NumberType>(str);
554 if (str.front() ==
'-')
556 str.remove_prefix(1);
557 auto result = detail::to_unsigned_float<NumberType>(str);
563 return detail::to_unsigned_float<NumberType>(str);
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 starts_with_nocase(string_view str, string_view prefix) noexcept
Determine whether a string starts with another string.
Definition: substring_checks.h:313
constexpr optional< NumberType > to_number(gul14::string_view str) noexcept
Convert an ASCII string_view into a number.
Definition: to_number.h:493
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().