<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

is_specialized

true

Type has a specialization

is_signed

false

Type is unsigned

is_integer

true

Type represents integers

is_exact

true

Type uses exact representations

has_infinity

false

Type cannot represent infinity

has_quiet_NaN

false

Type cannot represent quiet NaN

has_signaling_NaN

false

Type cannot represent signaling NaN

has_denorm

std::denorm_absent

Type does not have denormalized values

has_denorm_loss

false

No denormalization loss

round_style

std::round_toward_zero

Rounding truncates toward zero

is_iec559

false

Not IEC 559 (IEEE 754) compliant

is_bounded

true

Type has finite range

is_modulo

false

Overflow does not wrap (throws instead)

radix

2

Binary representation

min_exponent

0

Not applicable for integers

min_exponent10

0

Not applicable for integers

max_exponent

0

Not applicable for integers

max_exponent10

0

Not applicable for integers

traps

platform-dependent

Whether arithmetic traps

tinyness_before

false

Not applicable for integers

Type-Specific Values

Type digits digits10 max_digits10

u8

8

2

3

u16

16

4

5

u32

32

9

10

u64

64

18

20

u128

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

Example 1. This example demonstrates 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