/** * Utility functions for managing numeric types - such as converting between * floating-point and integer types. * * None of these are particularly complex functions, but storing them in a * single header with descriptive names helps the reader quickly recognize what * is being done. */ #pragma once #include #include #include #include #include #include #include namespace jvalidate::detail { /** * @brief Determine if a floating point number is actually an integer (in the * mathematical sense). */ inline bool is_json_integer(double number) { return std::floor(number) == number; } /** * @brief Determine if a floating point number is actually an integer, and * actually fits in the 64-bit integer type that we use for JSON Integer. */ inline bool fits_in_integer(double number) { static constexpr double g_int_max = std::numeric_limits::max(); static constexpr double g_int_min = std::numeric_limits::min(); return is_json_integer(number) && number <= g_int_max && number >= g_int_min; } /** * @brief Determine if an unsigned integer fits into a signed integer */ inline bool fits_in_integer(uint64_t number) { return (number & 0x8000'0000'0000'0000) == 0; } struct parse_integer_args { int base = 10; out read = discard_out; }; template static expected parse_integer(std::string_view in, parse_integer_args args = {}) { T rval = 0; auto [ptr, ec] = std::from_chars(in.begin(), in.end(), rval, args.base); args.read = ptr - in.begin(); if (ec != std::errc{}) { return unexpected(ec); } return rval; } }