Random Number Generation

Description

The library provides a boost::random::uniform_int_distribution specialization for all safe integer types, enabling uniform random number generation that returns safe integers directly. This works with any standard or Boost random engine (std::mt19937, std::mt19937_64, boost::random::mt19937, etc.).

All unsigned types (u8, u16, u32, u64, u128), signed types (i8, i16, i32, i64, i128), and bounded_uint<Min, Max> are supported.

The header <boost/safe_numbers/random.hpp> is NOT included in the convenience header <boost/safe_numbers.hpp>, because it requires Boost.Random to be present. You must include it explicitly.
#include <boost/safe_numbers.hpp>        // Does NOT include random support
#include <boost/safe_numbers/random.hpp> // Must be included separately

Synopsis

#include <boost/safe_numbers/random.hpp>

namespace boost::random {

template <typename SafeT>
    requires (safe_numbers::detail::is_unsigned_library_type_v<SafeT> ||
              safe_numbers::detail::is_signed_library_type_v<SafeT>)
class uniform_int_distribution<SafeT>
{
public:
    class param_type
    {
    public:
        explicit param_type(
            SafeT min = std::numeric_limits<SafeT>::min(),
            SafeT max = std::numeric_limits<SafeT>::max());

        auto a() const -> SafeT;
        auto b() const -> SafeT;

        friend bool operator==(const param_type& lhs, const param_type& rhs);
        friend bool operator!=(const param_type& lhs, const param_type& rhs);

        // Stream I/O
        template <class CharT, class Traits>
        friend std::basic_ostream<CharT, Traits>&
        operator<<(std::basic_ostream<CharT, Traits>& os, const param_type& p);

        template <class CharT, class Traits>
        friend std::basic_istream<CharT, Traits>&
        operator>>(std::basic_istream<CharT, Traits>& is, param_type& p);
    };

    explicit uniform_int_distribution(
        SafeT min = std::numeric_limits<SafeT>::min(),
        SafeT max = std::numeric_limits<SafeT>::max());

    explicit uniform_int_distribution(const param_type& param);

    // Generation
    template <typename Engine>
    auto operator()(Engine& eng) const -> SafeT;

    template <typename Engine>
    auto operator()(Engine& eng, const param_type& param) const -> SafeT;

    // Observers
    auto min() const -> SafeT;
    auto max() const -> SafeT;
    auto a() const -> SafeT;
    auto b() const -> SafeT;
    param_type param() const;
    void param(const param_type& p);
    void reset();

    // Comparison
    friend bool operator==(const uniform_int_distribution& lhs,
                           const uniform_int_distribution& rhs);
    friend bool operator!=(const uniform_int_distribution& lhs,
                           const uniform_int_distribution& rhs);

    // Stream I/O
    template <class CharT, class Traits>
    friend std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& os,
               const uniform_int_distribution& ud);

    template <class CharT, class Traits>
    friend std::basic_istream<CharT, Traits>&
    operator>>(std::basic_istream<CharT, Traits>& is,
               uniform_int_distribution& ud);
};

} // namespace boost::random

Bounded Types

For bounded_uint<Min, Max>, the default range is the declared bounds of the type:

using percent = bounded_uint<u8{0}, u8{100}>;

boost::random::uniform_int_distribution<percent> pct_dist;
auto pct = pct_dist(rng); // percent in [0, 100]

Examples

Example 1. This example demonstrates generating uniformly distributed safe integer values.
// 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.hpp>
#include <boost/safe_numbers/random.hpp>
#include <iostream>
#include <random>

int main()
{
    using namespace boost::safe_numbers;

    std::mt19937_64 rng{42};

    // Generate uniformly distributed unsigned values
    boost::random::uniform_int_distribution<u32> u32_dist{u32{1}, u32{100}};
    std::cout << "u32 in [1, 100]:  " << u32_dist(rng) << std::endl;

    // Generate uniformly distributed signed values
    boost::random::uniform_int_distribution<i32> i32_dist{i32{-50}, i32{50}};
    std::cout << "i32 in [-50, 50]: " << i32_dist(rng) << std::endl;

    // Default range uses the full range of the type
    boost::random::uniform_int_distribution<u64> u64_dist;
    std::cout << "u64 full range:   " << u64_dist(rng) << std::endl;

    // Use param_type to change range without reconstructing
    using dist_t = boost::random::uniform_int_distribution<u16>;
    dist_t dist;
    dist_t::param_type narrow{u16{0}, u16{10}};
    std::cout << "u16 in [0, 10]:   " << dist(rng, narrow) << std::endl;

    // 128-bit types work too
    boost::random::uniform_int_distribution<u128> u128_dist{u128{0U}, u128{1000U}};
    std::cout << "u128 in [0, 1000]: " << u128_dist(rng) << std::endl;

    return 0;
}

Output:

u32 in [1, 100]:  76
i32 in [-50, 50]: 14
u64 full range:   13874630024467741450
u16 in [0, 10]:   1
u128 in [0, 1000]: 904