<limits> Support
Description
The library provides std::numeric_limits specializations for all safe integer types.
These specializations delegate to the underlying type’s numeric_limits, ensuring consistent behavior with the built-in types.
#include <boost/safe_numbers/limits.hpp>
namespace std {
template <>
struct numeric_limits<boost::safe_numbers::u8>;
template <>
struct numeric_limits<boost::safe_numbers::u16>;
template <>
struct numeric_limits<boost::safe_numbers::u32>;
template <>
struct numeric_limits<boost::safe_numbers::u64>;
template <>
struct numeric_limits<boost::safe_numbers::u128>;
} // namespace std
Specialization
Each specialization has the following structure, where T is the safe integer type and basis_type is its underlying integer type:
namespace std {
template <>
struct numeric_limits<T>
{
using basis_type = typename T::basis_type;
// Static member constants
static constexpr bool is_specialized = std::numeric_limits<basis_type>::is_specialized;
static constexpr bool is_signed = std::numeric_limits<basis_type>::is_signed;
static constexpr bool is_integer = std::numeric_limits<basis_type>::is_integer;
static constexpr bool is_exact = std::numeric_limits<basis_type>::is_exact;
static constexpr bool has_infinity = std::numeric_limits<basis_type>::has_infinity;
static constexpr bool has_quiet_NaN = std::numeric_limits<basis_type>::has_quiet_NaN;
static constexpr bool has_signaling_NaN = std::numeric_limits<basis_type>::has_signaling_NaN;
// Deprecated in C++23
static constexpr std::float_denorm_style has_denorm = std::numeric_limits<basis_type>::has_denorm;
static constexpr bool has_denorm_loss = std::numeric_limits<basis_type>::has_denorm_loss;
static constexpr std::float_round_style round_style = std::numeric_limits<basis_type>::round_style;
static constexpr bool is_iec559 = std::numeric_limits<basis_type>::is_iec559;
static constexpr bool is_bounded = std::numeric_limits<basis_type>::is_bounded;
static constexpr bool is_modulo = std::numeric_limits<basis_type>::is_modulo;
static constexpr int digits = std::numeric_limits<basis_type>::digits;
static constexpr int digits10 = std::numeric_limits<basis_type>::digits10;
static constexpr int max_digits10 = std::numeric_limits<basis_type>::max_digits10;
static constexpr int radix = std::numeric_limits<basis_type>::radix;
static constexpr int min_exponent = std::numeric_limits<basis_type>::min_exponent;
static constexpr int min_exponent10 = std::numeric_limits<basis_type>::min_exponent10;
static constexpr int max_exponent = std::numeric_limits<basis_type>::max_exponent;
static constexpr int max_exponent10 = std::numeric_limits<basis_type>::max_exponent10;
static constexpr bool traps = std::numeric_limits<basis_type>::traps;
static constexpr bool tinyness_before = std::numeric_limits<basis_type>::tinyness_before;
// Static member functions
static constexpr T min() noexcept;
static constexpr T max() noexcept;
static constexpr T lowest() noexcept;
static constexpr T epsilon() noexcept;
static constexpr T round_error() noexcept;
static constexpr T infinity() noexcept;
static constexpr T quiet_NaN() noexcept;
static constexpr T signaling_NaN() noexcept;
static constexpr T denorm_min() noexcept;
};
} // namespace std
Member Constants
For unsigned integer types, the following values are consistent across all specializations:
| Member | Value | Description |
|---|---|---|
|
|
Type has a specialization |
|
|
Type is unsigned |
|
|
Type represents integers |
|
|
Type uses exact representations |
|
|
Type cannot represent infinity |
|
|
Type cannot represent quiet NaN |
|
|
Type cannot represent signaling NaN |
|
|
Type does not have denormalized values |
|
|
No denormalization loss |
|
|
Rounding truncates toward zero |
|
|
Not IEC 559 (IEEE 754) compliant |
|
|
Type has finite range |
|
|
Overflow does not wrap (throws instead) |
|
|
Binary representation |
|
|
Not applicable for integers |
|
|
Not applicable for integers |
|
|
Not applicable for integers |
|
|
Not applicable for integers |
|
platform-dependent |
Whether arithmetic traps |
|
|
Not applicable for integers |
Type-Specific Values
| Type | digits |
digits10 |
max_digits10 |
|---|---|---|---|
|
8 |
2 |
3 |
|
16 |
4 |
5 |
|
32 |
9 |
10 |
|
64 |
18 |
20 |
|
128 |
38 |
39 |
Member Functions
static constexpr T min() noexcept;
Returns the minimum finite value (always T{0} for unsigned types).
static constexpr T max() noexcept;
Returns the maximum finite value.
static constexpr T lowest() noexcept;
Returns the lowest finite value (same as min() for unsigned types).
static constexpr T epsilon() noexcept;
Returns T{0} (not meaningful for integer types).
static constexpr T round_error() noexcept;
Returns T{0} (not meaningful for integer types).
static constexpr T infinity() noexcept;
Returns T{0} (unsigned integers cannot represent infinity).
static constexpr T quiet_NaN() noexcept;
Returns T{0} (unsigned integers cannot represent NaN).
static constexpr T signaling_NaN() noexcept;
Returns T{0} (unsigned integers cannot represent NaN).
static constexpr T denorm_min() noexcept;
Returns T{0} (not meaningful for integer types).
Bounded Integer Specialization
A partial specialization of std::numeric_limits is provided for all bounded_uint<Min, Max> types.
Static member constants delegate to the underlying hardware type’s std::numeric_limits specialization, just as they do for the safe unsigned integer types.
The key difference is in the min() and max() member functions: rather than returning the hardware type’s full range, they return the compile-time bounds Min and Max.
namespace std {
template <auto Min, auto Max>
class numeric_limits<boost::safe_numbers::bounded_uint<Min, Max>>
{
// Static member constants delegate to std::numeric_limits<underlying_type>
// ...
// Returns bounded_uint constructed from Min
static constexpr bounded_uint<Min, Max> min();
// Returns bounded_uint constructed from Max
static constexpr bounded_uint<Min, Max> max();
// Same as min() for unsigned types
static constexpr bounded_uint<Min, Max> lowest();
// Not meaningful for integer types; returns min()
static constexpr bounded_uint<Min, Max> epsilon();
static constexpr bounded_uint<Min, Max> round_error();
static constexpr bounded_uint<Min, Max> infinity();
static constexpr bounded_uint<Min, Max> quiet_NaN();
static constexpr bounded_uint<Min, Max> signaling_NaN();
static constexpr bounded_uint<Min, Max> denorm_min();
};
} // namespace std
The non-applicable member functions (epsilon, round_error, infinity, quiet_NaN, signaling_NaN, denorm_min) return min() rather than a zero-constructed value, since zero may fall outside the valid range for types with a non-zero lower bound.
|
Verified Integer Specialization
A partial specialization of std::numeric_limits is provided for all verified_type_basis<BasisType> types, including both verified_u8-verified_u128 and verified_bounded_integer<Min, Max>.
namespace std {
template <boost::safe_numbers::detail::library_type BasisType>
class numeric_limits<boost::safe_numbers::detail::verified_type_basis<BasisType>>
{
using type = boost::safe_numbers::detail::verified_type_basis<BasisType>;
using basis_limits = std::numeric_limits<BasisType>;
// Static member constants delegate to std::numeric_limits<BasisType>
// ...
static constexpr type min();
static constexpr type max();
static constexpr type lowest();
// ... remaining functions delegate to basis_limits
};
} // namespace std
Static properties and functions delegate to std::numeric_limits<BasisType> (the safe integer basis type, not the underlying fundamental type).
This means verified bounded types correctly report their bounded min() and max() values.
The static constexpr member functions work because constructing the verified type with a constant expression invokes the consteval constructor as an immediate invocation, which is permitted in a constexpr context.
Example
std::numeric_limits with bounded integer types.// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/safe_numbers/bounded_integers.hpp>
#include <boost/safe_numbers/limits.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <limits>
int main()
{
using boost::safe_numbers::bounded_uint;
using percent = bounded_uint<0u, 100u>;
std::cout << "percent type:" << std::endl;
std::cout << " min: " << std::numeric_limits<percent>::min() << std::endl;
std::cout << " max: " << std::numeric_limits<percent>::max() << std::endl;
std::cout << " lowest: " << std::numeric_limits<percent>::lowest() << std::endl;
std::cout << " digits: " << std::numeric_limits<percent>::digits << std::endl;
std::cout << std::endl;
using port = bounded_uint<1u, 65535u>;
std::cout << "port type:" << std::endl;
std::cout << " min: " << std::numeric_limits<port>::min() << std::endl;
std::cout << " max: " << std::numeric_limits<port>::max() << std::endl;
std::cout << " lowest: " << std::numeric_limits<port>::lowest() << std::endl;
std::cout << " digits: " << std::numeric_limits<port>::digits << std::endl;
return 0;
}
Output:
percent type: min: 0 max: 100 lowest: 0 digits: 8 port type: min: 1 max: 65535 lowest: 1 digits: 16