General Utility Library for C++14  2.12
to_number.h
Go to the documentation of this file.
1 
23 #ifndef GUL14_TO_NUMBER_H_
24 #define GUL14_TO_NUMBER_H_
25 
26 #include <array>
27 #include <cmath>
28 #include <cstdint>
29 #include <cstdlib>
30 #include <exception>
31 #include <limits>
32 #include <type_traits>
33 
34 #include "gul14/internal.h"
35 #include "gul14/optional.h"
36 #include "gul14/string_view.h"
37 #include "gul14/substring_checks.h"
38 
39 namespace gul14 {
40 
48 namespace detail {
49 
50 constexpr inline bool is_digit(char c) noexcept
51 {
52  return c >= '0' && c <= '9';
53 }
54 
55 constexpr inline bool is_nan_specifier(char c) noexcept
56 {
57  if (c >= '0' && c <= '9')
58  return true;
59  if (c >= 'a' && c <= 'z')
60  return true;
61  if (c >= 'A' && c <= 'Z')
62  return true;
63  if (c == '_')
64  return true;
65  return false;
66 }
67 
68 template <typename NumberType, bool count_magnitude = false>
69 constexpr inline optional<NumberType> to_unsigned_integer(gul14::string_view str,
70  NumberType* magnitude = nullptr) noexcept
71 {
72 #ifndef __GNUC__
73  constexpr NumberType max_tenth = std::numeric_limits<NumberType>::max() / 10;
74 #endif
75 
76  if (str.empty())
77  return nullopt;
78 
79  NumberType result = 0;
80 
81  for (char c : str)
82  {
83  if (!is_digit(c))
84  return nullopt;
85 
86 #ifdef __GNUC__
87  if (__builtin_mul_overflow(result, NumberType{ 10 }, &result)) // NOLINT(cppcoreguidelines-pro-type-vararg)
88  return nullopt;
89 
90  if (__builtin_add_overflow(result, static_cast<NumberType>(c - '0'), &result)) // NOLINT(cppcoreguidelines-pro-type-vararg)
91  return nullopt;
92 #else
93  if (result > max_tenth)
94  return nullopt;
95 
96  result *= 10;
97 
98  auto last = result;
99 
100  result += c - '0';
101  if (result < last)
102  return nullopt;
103 #endif
104  if /*constexpr*/ (count_magnitude)
105  *magnitude *= NumberType{ 10 };
106  }
107 
108  return result;
109 }
110 
111 /* Parse a signed exponent specifier.
112  * May start with a leading sign ('+' or '-'). The exponent value is limited to
113  * the range of int. The used range with a long double conversion is usually in
114  * the range -5000 to 5000, so this is not really a limitation.
115  */
116 constexpr optional<int> parse_exponent(string_view str) noexcept
117 {
118  bool negative = false;
119 
120  switch (str.front())
121  {
122  case '+':
123  str.remove_prefix(1);
124  break;
125  case '-':
126  str.remove_prefix(1);
127  negative = true;
128  break;
129  default:
130  break;
131  }
132 
133  auto opt_exp = to_unsigned_integer<int>(str);
134 
135  if (!opt_exp)
136  return nullopt;
137 
138  if (negative)
139  return -*opt_exp;
140  return *opt_exp;
141 }
142 
143 // For some 'long double' types with a big mantissa uint64 is not large enough.
144 // We resort to __uint128, which is a non standard extension in GCC and clang.
145 // But only if we need to.
146 // Note that on some compilers there are no std::numeric_limits<> for the extension
147 // type, and then asserts later on will fail. But usually that compilers have small
148 // long double types.
149 template <typename NumberType>
150 using FloatConversionIntType =
151  typename std::conditional<
152  (std::numeric_limits<std::uint64_t>::digits10 >= std::numeric_limits<NumberType>::digits10),
153  std::uint64_t,
154  #ifdef __SIZEOF_INT128__ // GCC, clang, intel
155  __uint128_t
156  #else
157  std::uint64_t
158  #endif
159  >::type;
160 
177 template <typename NumberType>
178 constexpr inline gul14::optional<NumberType> to_normalized_float(gul14::string_view i1, gul14::string_view i2) noexcept
179 {
180  static_assert(std::numeric_limits<FloatConversionIntType<NumberType>>::digits10
181  >= std::numeric_limits<NumberType>::digits10,
182  "FloatConversionIntType is too small for NumberType");
183 
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()));
188 
189  FloatConversionIntType<NumberType> accu{ 0 };
190 
191  auto magnitude = FloatConversionIntType<NumberType>{ 1 };
192 
193  if (not i2.empty()) {
194  auto f2 = to_unsigned_integer<FloatConversionIntType<NumberType>, true>(i2, &magnitude);
195  if (not f2.has_value())
196  return nullopt;
197  accu = *f2;
198  }
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())
203  return nullopt;
204  accu += (*f1 * i2_magnitude);
205  }
206 
207  return static_cast<NumberType>(accu) / static_cast<NumberType>(magnitude / 10); // NOLINT(bugprone-integer-division): Precision loss is not possible with normalized accu
208 }
209 
210 template <typename NumberType>
211 struct ParseInfNanResult {
212  bool result_valid;
213  optional<NumberType> result;
214 };
215 
235 template <typename NumberType>
236 constexpr inline ParseInfNanResult<NumberType> parse_inf_nan(gul14::string_view str) noexcept
237 {
238  auto const strlength = str.length();
239  if (strlength == 0)
240  return { true, {} };
241 
242  if (gul14::starts_with_nocase(str, "inf")) {
243  if (strlength == 3 /* strlen("inf") */ )
244  return { true, make_optional(std::numeric_limits<NumberType>::infinity()) };
245  if (strlength == 8 /* strlen("infinity") */
246  and gul14::starts_with_nocase(str.substr(3), "inity"))
247  return { true, make_optional(std::numeric_limits<NumberType>::infinity()) };
248  return { true, {} };
249  }
250 
251  if (gul14::starts_with_nocase(str, "nan")) {
252  if (strlength == 3 /* strlen("nan") */ )
253  return { true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
254  if (strlength < 5 /* strlen("nan()") */ or str[3] != '(' or str.back() != ')')
255  return { true, {} };
256  str.remove_prefix(4);
257  str.remove_suffix(1);
258  while (str.length()) {
259  if (not is_nan_specifier(str.front()))
260  return { true, {} };
261  str.remove_prefix(1);
262  }
263  // We do not use the NaN specifier
264  return { true, make_optional(std::numeric_limits<NumberType>::quiet_NaN()) };
265  }
266  return { false, {} };
267 }
268 
280 GUL_EXPORT
281 long double pow10(int exponent);
282 
300 template <typename NumberType>
301 constexpr inline optional<NumberType> to_unsigned_float(gul14::string_view str) noexcept
302 {
303  auto inf_nan = parse_inf_nan<NumberType>(str);
304  if (inf_nan.result_valid)
305  return inf_nan.result;
306 
307  int exponent = 0;
308  auto e_pos = str.find_first_of("eE");
309  if (e_pos != gul14::string_view::npos)
310  {
311  if (e_pos + 1 == str.size())
312  return nullopt;
313 
314  auto str_exponent = str.substr(e_pos + 1);
315 
316  str = str.substr(0, e_pos);
317 
318  auto opt_exp = detail::parse_exponent(str_exponent);
319 
320  if (!opt_exp)
321  return nullopt;
322 
323  exponent = *opt_exp;
324  }
325 
326  gul14::string_view str_before_point{ str };
327  gul14::string_view str_after_point;
328 
329  auto point_pos = str.find('.');
330  if (point_pos != gul14::string_view::npos)
331  {
332  str_before_point = str.substr(0, point_pos);
333  str_after_point = str.substr(point_pos + 1);
334  }
335 
336  if (str_before_point.empty() && str_after_point.empty())
337  return nullopt;
338 
339  // Get rid of leading zeros
340  while (!str_before_point.empty() and str_before_point[0] == '0')
341  str_before_point.remove_prefix(1);
342 
343  // Normalize the number
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);
348 
349  if (str_after_point.empty())
350  return { 0 };
351 
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());
355  } else {
356  exponent += static_cast<int>(str_before_point.length() - 1);
357  }
358 
359  // Now the incoming number string is like this:
360  // "s.tr_before_point" "str_after_point" E exponent
361  // ^ ^
362  // | here is the decimal dot, virtually | corrected exponent
363 
364  using long_double = long double;
365  using CalcType = std::conditional_t<
366  std::greater<>()(sizeof(NumberType), sizeof(double)),
367  long_double, double>;
368 
369  auto norm_val = to_normalized_float<CalcType>(str_before_point, str_after_point);
370  if (not norm_val.has_value())
371  return nullopt;
372 
373  return detail::pow10(exponent) * *norm_val;
374 }
375 
393 template <typename NumberType>
394 inline optional<NumberType> strtold_wrapper(gul14::string_view str) noexcept
395 {
396  if (str.empty())
397  return nullopt;
398 
399  try
400  {
401  auto input = std::string{ str };
402  char* process_end;
403  auto value = static_cast<NumberType>(std::strtold(input.c_str(), &process_end));
404 
405  if (input.data() + input.size() != process_end) // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic): Pointer arithmetic needed because strtold gives pointer back
406  return nullopt;
407  return value;
408  }
409  catch (const std::exception &)
410  {
411  return nullopt;
412  }
413 }
414 
415 } // namespace detail
417 
418 
495 // Overload for unsigned integer types.
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>>
501 {
502  return detail::to_unsigned_integer<NumberType>(str);
503 }
504 
505 // Overload for signed integer types.
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>>
510 to_number(gul14::string_view str) noexcept
511 {
512  if (str.empty())
513  return nullopt;
514 
515  if (str.front() == '-')
516  {
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;
520 
521  str.remove_prefix(1);
522 
523  auto result = detail::to_unsigned_integer<UnsignedT>(str);
524  if (!result)
525  return nullopt;
526 
527  if (*result == max_abs_negative_value)
528  return std::numeric_limits<NumberType>::lowest();
529  else if (*result > max_abs_negative_value)
530  return nullopt;
531 
532  return static_cast<NumberType>(-static_cast<NumberType>(*result));
533  }
534 
535  return detail::to_unsigned_integer<NumberType>(str);
536 }
537 
538 // Overload for floating-point types.
539 template <typename NumberType>
540 constexpr inline std::enable_if_t<std::is_floating_point<NumberType>::value,
541  optional<NumberType>>
542 to_number(gul14::string_view str) noexcept
543 {
544  if (str.empty())
545  return nullopt;
546 
547  if (
548 #ifdef _MSC_VER
549 # pragma warning( push )
550 # pragma warning( disable: 4127 ) // conditional expression is constant
551 #endif
552  (std::numeric_limits<detail::FloatConversionIntType<NumberType>>::digits10
553  <= std::numeric_limits<NumberType>::digits10)) {
554 #ifdef _MSC_VER
555 # pragma warning( pop )
556 #endif
557  // Too big for our approach. Resort to non-constexpr functionality.
558  // This actually never happenes with the currently supported platforms / compilers.
559  // (Except long double on Darwin)
560  return detail::strtold_wrapper<NumberType>(str);
561  }
562 
563  if (str.front() == '-')
564  {
565  str.remove_prefix(1);
566  auto result = detail::to_unsigned_float<NumberType>(str);
567  if (!result)
568  return nullopt;
569  return -*result;
570  }
571 
572  return detail::to_unsigned_float<NumberType>(str);
573 }
574 
575 // Overload for bool
576 template<>
577 constexpr inline optional<bool> to_number<bool>(gul14::string_view str) noexcept
578 {
579  if (str.length() == 1) {
580  if (str[0] == '1')
581  return true;
582  if (str[0] == '0')
583  return false;
584  return nullopt;
585  }
586  if (equals_nocase(str, "true"))
587  return true;
588 
589  if (equals_nocase(str, "false"))
590  return false;
591 
592  return nullopt;
593 }
594 
596 
597 } // namespace gul14
598 
599 #endif
600 
601 // vi:ts=4:sw=4:et:sts=4
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().