<limits> Support

Description

The library provides std::numeric_limits specializations for all safe integer types, for the safe floating-point types f32 and f64, and for the bounded 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>;

template <>
struct numeric_limits<boost::safe_numbers::f32>;

template <>
struct numeric_limits<boost::safe_numbers::f64>;

} // 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
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T min() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T max() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T lowest() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T epsilon() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T round_error() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T infinity() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T quiet_NaN() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T signaling_NaN() noexcept;
    BOOST_SAFE_NUMBERS_HOST_DEVICE 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

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T min() noexcept;

Returns the minimum finite value (always T{0} for unsigned types).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T max() noexcept;

Returns the maximum finite value.

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T lowest() noexcept;

Returns the lowest finite value (same as min() for unsigned types).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T epsilon() noexcept;

Returns T{0} (not meaningful for integer types).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T round_error() noexcept;

Returns T{0} (not meaningful for integer types).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T infinity() noexcept;

Returns T{0} (unsigned integers cannot represent infinity).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T quiet_NaN() noexcept;

Returns T{0} (unsigned integers cannot represent NaN).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T signaling_NaN() noexcept;

Returns T{0} (unsigned integers cannot represent NaN).

BOOST_SAFE_NUMBERS_HOST_DEVICE static constexpr T denorm_min() noexcept;

Returns T{0} (not meaningful for integer types).

Floating-Point Specializations

The specializations for f32 and f64 use the same structure shown above, delegating every member to std::numeric_limits<float> and std::numeric_limits<double> respectively. Unlike the integer specializations, the floating-point members therefore carry their genuine IEEE 754 meaning: the queries below reflect floating-point properties, and the member functions return real floating-point values rather than T{0}.

Member f32 and f64 value Description

is_signed

true

Floating-point types are signed

is_integer

false

Type does not represent integers

is_exact

false

Type uses an approximate representation

has_infinity

true

Type can represent positive and negative infinity

has_quiet_NaN

true

Type can represent a quiet NaN

has_signaling_NaN

true

Type can represent a signaling NaN

is_iec559

true

Type conforms to IEC 559 (IEEE 754)

is_bounded

true

Type has a finite set of representable values

is_modulo

false

Arithmetic does not wrap

round_style

std::round_to_nearest

Default IEEE 754 rounding

radix

2

Binary representation

The type-specific values match the underlying float and double:

Type digits digits10 max_digits10 min_exponent max_exponent min_exponent10 max_exponent10

f32

24

6

9

-125

128

-37

38

f64

53

15

17

-1021

1024

-307

308

For these types the member functions return meaningful floating-point values wrapped in the safe type:

Function Returns

min()

The smallest positive normal value

max()

The largest finite value

lowest()

The most negative finite value (-max())

epsilon()

The difference between 1.0 and the next representable value

round_error()

The maximum rounding error (0.5 in the default rounding mode)

infinity()

Positive infinity

quiet_NaN()

A quiet NaN

signaling_NaN()

A signaling NaN

denorm_min()

The smallest positive subnormal value

infinity(), quiet_NaN(), and signaling_NaN() construct a non-finite f32 or f64. Construction does not validate the value (see Floating-Point Types), so these values are produced without throwing; an exception is only raised when such a value is later observed by a checked arithmetic or <cmath> operation.

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.

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

Bounded Floating-Point Specialization

A partial specialization of std::numeric_limits is also provided for bounded_float<Min, Max> on platforms where the bounded floating-point type is available. It mirrors the bounded integer specialization: the min() and max() member functions return the compile-time bounds Min and Max rather than the underlying type’s full range. See Bounded Floating-Point Types.

Because a bounded value is a constrained subset of the underlying float or double, several queries differ from the unbounded floating-point specializations:

Member Value Description

is_integer

false

Type does not represent integers

is_exact

false

Type uses an approximate representation

has_infinity

false

A bounded value cannot be infinite

has_quiet_NaN

false

A bounded value cannot be NaN

has_signaling_NaN

false

A bounded value cannot be NaN

is_iec559

false

The constrained range is not a full IEC 559 type

is_bounded

true

Type has a finite range

is_modulo

false

Arithmetic does not wrap

namespace std {

template <auto Min, auto Max>
class numeric_limits<boost::safe_numbers::bounded_float<Min, Max>>
{
    // Static member constants delegate to std::numeric_limits<underlying_type>,
    // except for the queries listed above

    // Returns bounded_float constructed from Min
    static constexpr bounded_float<Min, Max> min();

    // Returns bounded_float constructed from Max
    static constexpr bounded_float<Min, Max> max();

    // Same as min()
    static constexpr bounded_float<Min, Max> lowest();

    // Not meaningful for a bounded range; each returns min()
    static constexpr bounded_float<Min, Max> epsilon();
    static constexpr bounded_float<Min, Max> round_error();
    static constexpr bounded_float<Min, Max> infinity();
    static constexpr bounded_float<Min, Max> quiet_NaN();
    static constexpr bounded_float<Min, Max> signaling_NaN();
    static constexpr bounded_float<Min, Max> denorm_min();
};

} // namespace std
As with the bounded integer specialization, 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.