number.h 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  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 <limits>
  14. #include <stdexcept>
  15. #include <string_view>
  16. #include <system_error>
  17. namespace jvalidate::detail {
  18. /**
  19. * @brief Determine if a floating point number is actually an integer (in the
  20. * mathematical sense).
  21. */
  22. inline bool is_json_integer(double number) { return std::floor(number) == number; }
  23. /**
  24. * @brief Determine if a floating point number is actually an integer, and
  25. * actually fits in the 64-bit integer type that we use for JSON Integer.
  26. */
  27. inline bool fits_in_integer(double number) {
  28. static constexpr double g_int_max = static_cast<double>(std::numeric_limits<int64_t>::max());
  29. static constexpr double g_int_min = static_cast<double>(std::numeric_limits<int64_t>::min());
  30. return is_json_integer(number) && number <= g_int_max && number >= g_int_min;
  31. }
  32. /**
  33. * @brief Determine if an unsigned integer fits into a signed integer
  34. */
  35. inline bool fits_in_integer(uint64_t number) { return (number & 0x8000'0000'0000'0000) == 0; }
  36. template <std::integral I> I from_str(std::string_view str, int base = 10) {
  37. I rval;
  38. auto [end, ec] = std::from_chars(str.begin(), str.end(), rval, base);
  39. if (ec != std::errc{}) {
  40. throw std::runtime_error(std::make_error_code(ec).message());
  41. }
  42. if (end != str.end()) {
  43. throw std::runtime_error("NaN: " + std::string(str));
  44. }
  45. return rval;
  46. }
  47. }