number.h 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. /**
  2. * Utility functions for managing numeric types - such as converting between
  3. * floating-point and integer types.
  4. *
  5. * None of these are particularly complex functions, but storing them in a
  6. * single header with descriptive names helps the reader quickly recognize what
  7. * is being done.
  8. */
  9. #pragma once
  10. #include <charconv>
  11. #include <cmath>
  12. #include <concepts>
  13. #include <cstdint>
  14. #include <limits>
  15. #include <string_view>
  16. #include <system_error>
  17. #include <jvalidate/compat/expected.h>
  18. #include <jvalidate/detail/out.h>
  19. namespace jvalidate::detail {
  20. /**
  21. * @brief Determine if a floating point number is actually an integer (in the
  22. * mathematical sense).
  23. */
  24. inline bool is_json_integer(double number) { return std::floor(number) == number; }
  25. /**
  26. * @brief Determine if a floating point number is actually an integer, and
  27. * actually fits in the 64-bit integer type that we use for JSON Integer.
  28. */
  29. inline bool fits_in_integer(double number) {
  30. static constexpr auto g_int_max = static_cast<double>(std::numeric_limits<int64_t>::max());
  31. static constexpr auto g_int_min = static_cast<double>(std::numeric_limits<int64_t>::min());
  32. return is_json_integer(number) && number <= g_int_max && number >= g_int_min;
  33. }
  34. /**
  35. * @brief Determine if an unsigned integer fits into a signed integer
  36. */
  37. // NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers)
  38. inline bool fits_in_integer(uint64_t number) { return (number & 0x8000'0000'0000'0000) == 0; }
  39. struct ParseIntegerArgs {
  40. int base = 10; // NOLINT(cppcoreguidelines-avoid-magic-numbers)
  41. out<size_t> read = discard_out;
  42. };
  43. template <std::integral T>
  44. static expected<T, std::errc> parse_integer(std::string_view in, ParseIntegerArgs args = {}) {
  45. T rval = 0;
  46. auto [ptr, ec] = std::from_chars(in.begin(), in.end(), rval, args.base);
  47. args.read = ptr - in.begin();
  48. if (ec != std::errc{}) {
  49. return unexpected(ec);
  50. }
  51. if (ptr != in.end()) {
  52. return unexpected(std::errc::result_out_of_range);
  53. }
  54. return rval;
  55. }
  56. }