23 #ifndef GUL14_TO_NUMBER_H_
24 #define GUL14_TO_NUMBER_H_
32 #include <type_traits>
44 constexpr
inline bool is_digit(
char c) noexcept
46 return c >=
'0' && c <=
'9';
49 constexpr
inline bool is_nan_specifier(
char c) noexcept
51 if (c >=
'0' && c <=
'9')
53 if (c >=
'a' && c <=
'z')
55 if (c >=
'A' && c <=
'Z')
62 template <
typename NumberType,
bool count_magnitude = false>
64 NumberType* magnitude =
nullptr) noexcept
67 constexpr NumberType max_tenth = std::numeric_limits<NumberType>::max() / 10;
73 NumberType result = 0;
81 if (__builtin_mul_overflow(result, NumberType{ 10 }, &result))
84 if (__builtin_add_overflow(result,
static_cast<NumberType
>(c -
'0'), &result))
87 if (result > max_tenth)
99 *magnitude *= NumberType{ 10 };
110 constexpr optional<int> parse_exponent(
string_view str) noexcept
112 bool negative =
false;
117 str.remove_prefix(1);
120 str.remove_prefix(1);
127 auto opt_exp = to_unsigned_integer<int>(str);
143 template <
typename NumberType>
144 using FloatConversionIntType =
145 typename std::conditional<
146 (std::numeric_limits<std::uint64_t>::digits10 >= std::numeric_limits<NumberType>::digits10),
148 #ifdef __SIZEOF_INT128__
171 template <
typename NumberType>
174 static_assert(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10
175 >= std::numeric_limits<NumberType>::digits10,
176 "FloatConversionIntType is too small for NumberType");
178 i1 = i1.substr(0, std::min(i1.length(),
179 size_t(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10)));
180 i2 = i2.substr(0, std::min(i2.length(),
181 size_t(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10) - i1.length()));
183 FloatConversionIntType<NumberType> accu{ 0 };
185 auto magnitude = FloatConversionIntType<NumberType>{ 1 };
187 if (not i2.empty()) {
188 auto f2 = to_unsigned_integer<FloatConversionIntType<NumberType>,
true>(i2, &magnitude);
189 if (not f2.has_value())
193 if (not i1.empty()) {
194 auto i2_magnitude = magnitude;
195 auto f1 = to_unsigned_integer<FloatConversionIntType<NumberType>,
true>(i1, &magnitude);
196 if (not f1.has_value())
198 accu += (*f1 * i2_magnitude);
201 return static_cast<NumberType
>(accu) /
static_cast<NumberType
>(magnitude / 10);
204 template <
typename NumberType>
205 struct ParseInfNanResult {
207 optional<NumberType> result;
229 template <
typename NumberType>
230 constexpr
inline ParseInfNanResult<NumberType> parse_inf_nan(
gul14::string_view str) noexcept
232 auto const strlength = str.length();
238 return {
true, make_optional(std::numeric_limits<NumberType>::infinity()) };
241 return {
true, make_optional(std::numeric_limits<NumberType>::infinity()) };
247 return {
true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
248 if (strlength < 5 or str[3] !=
'(' or str.back() !=
')')
250 str.remove_prefix(4);
251 str.remove_suffix(1);
252 while (str.length()) {
253 if (not is_nan_specifier(str.front()))
255 str.remove_prefix(1);
258 return {
true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
260 return {
false, {} };
280 template <
typename NumberType>
281 constexpr
inline optional<NumberType> to_unsigned_float(
gul14::string_view str) noexcept
283 auto inf_nan = parse_inf_nan<NumberType>(str);
284 if (inf_nan.result_valid)
285 return inf_nan.result;
288 auto e_pos = str.find_first_of(
"eE");
289 if (e_pos != gul14::string_view::npos)
291 if (e_pos + 1 == str.size())
294 auto str_exponent = str.substr(e_pos + 1);
296 str = str.substr(0, e_pos);
298 auto opt_exp = detail::parse_exponent(str_exponent);
309 auto point_pos = str.find(
'.');
310 if (point_pos != gul14::string_view::npos)
312 str_before_point = str.substr(0, point_pos);
313 str_after_point = str.substr(point_pos + 1);
316 if (str_before_point.empty() && str_after_point.empty())
320 while (!str_before_point.empty() and str_before_point[0] ==
'0')
321 str_before_point.remove_prefix(1);
324 if (str_before_point.empty()) {
325 auto const old_digits = str_after_point.length();
326 while (!str_after_point.empty() and str_after_point[0] ==
'0')
327 str_after_point.remove_prefix(1);
329 if (str_after_point.empty())
332 str_before_point = str_after_point.substr(0, 1);
333 str_after_point.remove_prefix(1);
334 exponent -=
static_cast<int>(old_digits - str_after_point.length());
336 exponent +=
static_cast<int>(str_before_point.length() - 1);
344 using long_double =
long double;
345 using CalcType = std::conditional_t<
346 std::greater<>()(
sizeof(NumberType),
sizeof(
double)),
347 long_double,
double>;
349 auto norm_val = to_normalized_float<CalcType>(str_before_point, str_after_point);
350 if (not norm_val.has_value())
353 return NumberType(std::pow(CalcType(10), CalcType(exponent)) * *norm_val);
373 template <
typename NumberType>
381 auto input = std::string{ str };
383 auto value =
static_cast<NumberType
>(std::strtold(input.c_str(), &process_end));
385 if (input.data() + input.size() != process_end)
389 catch (
const std::exception &)
470 template <
typename NumberType,
471 std::enable_if_t<std::is_integral<NumberType>::value &&
472 std::is_unsigned<NumberType>::value,
int> = 0>
475 return detail::to_unsigned_integer<NumberType>(str);
479 template <
typename NumberType,
480 std::enable_if_t<std::is_integral<NumberType>::value &&
481 std::is_signed<NumberType>::value,
int> = 0>
487 if (str.front() ==
'-')
489 using UnsignedT = std::make_unsigned_t<NumberType>;
490 constexpr
auto max_abs_negative_value =
491 static_cast<UnsignedT
>(std::numeric_limits<NumberType>::max()) + 1;
493 str.remove_prefix(1);
495 auto result = detail::to_unsigned_integer<UnsignedT>(str);
499 if (*result == max_abs_negative_value)
500 return std::numeric_limits<NumberType>::lowest();
501 else if (*result > max_abs_negative_value)
504 return static_cast<NumberType
>(-
static_cast<NumberType
>(*result));
507 return detail::to_unsigned_integer<NumberType>(str);
511 template <
typename NumberType,
512 std::enable_if_t<std::is_floating_point<NumberType>::value,
int> = 0>
522 (
sizeof(NumberType) >
sizeof(
double)) ||
525 # pragma warning( push )
526 # pragma warning( disable: 4127 )
528 (std::numeric_limits<detail::FloatConversionIntType<NumberType>>::digits10
529 <= std::numeric_limits<NumberType>::digits10)) {
531 # pragma warning( pop )
536 return detail::strtold_wrapper<NumberType>(str);
539 if (str.front() ==
'-')
541 str.remove_prefix(1);
542 auto result = detail::to_unsigned_float<NumberType>(str);
548 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:268
Definition of macros used internally by GUL.
Namespace gul14 contains all functions and classes of the General Utility Library.
Definition: doxygen.h:26
constexpr optional< NumberType > to_number(gul14::string_view str) noexcept
Convert an ASCII string_view into a number.
Definition: to_number.h:473
basic_string_view< char > string_view
A view to a contiguous sequence of chars.
Definition: string_view.h:623
constexpr bool starts_with_nocase(string_view str, string_view prefix) noexcept
Determine whether a string starts with another string.
Definition: substring_checks.h:307
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().