Examples

All the following examples can be found in the examples/ folder of the library.

Basic Usage (Default Throwing Behavior)

Example 1. This example demonstrates the default behavior where arithmetic overflow throws an exception.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/safe_numbers/unsigned_integers.hpp>     // For safe_numbers types
#include <boost/safe_numbers/iostream.hpp>              // For safe_numbers <iostream> support
#include <iostream>
#include <cstdint>

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

    try
    {
        const u8 x {UINT8_MAX};
        const u8 y {2};
        const u8 z {x + y};

        std::cout << "Value of z: " << z << std::endl;
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error Detected: " << e.what() << std::endl;
    }

    return 0;
}

Output:

Error Detected: Overflow detected in unsigned addition

Construction and Conversion

Example 2. This example demonstrates safe integer construction and conversion.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// This example demonstrates how to construct a safe unsigned integer,
// and then convert it back to built-in types

#include <boost/safe_numbers/unsigned_integers.hpp> // For the safe unsigned integers
#include <boost/safe_numbers/iostream.hpp>          // For safe numbers support to <iostream>
#include <iostream>
#include <cstdint>

int main()
{
    using boost::safe_numbers::u32; // 32-bit unsigned safe integer

    // Construct a safe u32 from a builtin unsigned value explicitly
    constexpr u32 safe_value {42U};

    // Safe values support <iostream> in the same way that the builtins do
    std::cout << "Safe Value: " << safe_value << '\n';

    // To convert the safe value back to a builtin value
    // use the known basis, or generically via the basis_type typedef
    constexpr std::uint32_t unsafe_value {static_cast<std::uint32_t>(safe_value)};
    constexpr std::uint32_t unsafe_value_from_typedef {static_cast<u32::basis_type>(safe_value)};

    // The conversions are all completely constexpr
    // Invalid conversions such as narrowing will result in compile errors
    // instead of run-time error
    if constexpr (unsafe_value == unsafe_value_from_typedef)
    {
       std::cout << "Builtin Value: " << unsafe_value << '\n';
    }

    return 0;
}

Output:

Safe Value: 42
Builtin Value: 42

Saturating Arithmetic

Saturating arithmetic clamps results to the type’s minimum or maximum value instead of overflowing or throwing. This is useful when you want bounded behavior without exceptions.

Example 3. This example demonstrates saturating arithmetic operations.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[saturating_arithmetic_example
//` This example demonstrates the use of saturating arithmetic operations.
//` When overflow or underflow would occur, the result saturates at the
//` type's maximum or minimum value instead of wrapping or throwing.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>

int main()
{
    using boost::safe_numbers::u32;
    using boost::safe_numbers::saturating_add;
    using boost::safe_numbers::saturating_sub;
    using boost::safe_numbers::saturating_mul;

    // Saturating addition: clamps to max on overflow
    {
        const u32 a {std::numeric_limits<std::uint32_t>::max()};
        const u32 b {100U};
        const u32 result {saturating_add(a, b)};

        std::cout << "saturating_add(max, 100) = " << result << std::endl;
        // Output: 4294967295 (UINT32_MAX, saturated)
    }

    // Saturating subtraction: clamps to min (0) on underflow
    {
        const u32 a {10U};
        const u32 b {100U};
        const u32 result {saturating_sub(a, b)};

        std::cout << "saturating_sub(10, 100) = " << result << std::endl;
        // Output: 0 (saturated at minimum)
    }

    // Saturating multiplication: clamps to max on overflow
    {
        const u32 a {std::numeric_limits<std::uint32_t>::max()};
        const u32 b {2U};
        const u32 result {saturating_mul(a, b)};

        std::cout << "saturating_mul(max, 2) = " << result << std::endl;
        // Output: 4294967295 (UINT32_MAX, saturated)
    }

    // Normal operations that don't overflow work as expected
    {
        const u32 a {100U};
        const u32 b {50U};

        std::cout << "saturating_add(100, 50) = " << saturating_add(a, b) << std::endl;
        std::cout << "saturating_sub(100, 50) = " << saturating_sub(a, b) << std::endl;
        std::cout << "saturating_mul(100, 50) = " << saturating_mul(a, b) << std::endl;
        // Output: 150, 50, 5000
    }

    return 0;
}
//]

Output:

saturating_add(max, 100) = 4294967295
saturating_sub(10, 100) = 0
saturating_mul(max, 2) = 4294967295
saturating_add(100, 50) = 150
saturating_sub(100, 50) = 50
saturating_mul(100, 50) = 5000

Overflowing Arithmetic

Overflowing arithmetic returns both the wrapped result and a boolean flag indicating whether overflow occurred. This gives you access to both the C-style wrapped value and overflow detection in a single operation.

Example 4. This example demonstrates overflowing arithmetic operations.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[overflowing_arithmetic_example
//` This example demonstrates the use of overflowing arithmetic operations.
//` These functions return a std::pair containing the result and a boolean
//` flag indicating whether overflow/underflow occurred. The result is the
//` wrapped value (as if using normal unsigned arithmetic).

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <utility>

int main()
{
    using boost::safe_numbers::u32;
    using boost::safe_numbers::overflowing_add;
    using boost::safe_numbers::overflowing_sub;
    using boost::safe_numbers::overflowing_mul;

    // Overflowing addition: returns (wrapped_result, did_overflow)
    {
        const u32 a {std::numeric_limits<std::uint32_t>::max()};
        const u32 b {100U};
        const auto [result, overflowed] {overflowing_add(a, b)};

        std::cout << "overflowing_add(max, 100):" << std::endl;
        std::cout << "  result = " << result << std::endl;
        std::cout << "  overflowed = " << std::boolalpha << overflowed << std::endl;
        // Output: result = 99 (wrapped), overflowed = true
    }

    // Overflowing subtraction: returns (wrapped_result, did_underflow)
    {
        const u32 a {10U};
        const u32 b {100U};
        const auto [result, underflowed] {overflowing_sub(a, b)};

        std::cout << "overflowing_sub(10, 100):" << std::endl;
        std::cout << "  result = " << result << std::endl;
        std::cout << "  underflowed = " << std::boolalpha << underflowed << std::endl;
        // Output: result = 4294967206 (wrapped), underflowed = true
    }

    // Overflowing multiplication: returns (wrapped_result, did_overflow)
    {
        const u32 a {std::numeric_limits<std::uint32_t>::max()};
        const u32 b {2U};
        const auto [result, overflowed] {overflowing_mul(a, b)};

        std::cout << "overflowing_mul(max, 2):" << std::endl;
        std::cout << "  result = " << result << std::endl;
        std::cout << "  overflowed = " << std::boolalpha << overflowed << std::endl;
        // Output: result = 4294967294 (wrapped), overflowed = true
    }

    // Normal operations that don't overflow
    {
        const u32 a {100U};
        const u32 b {50U};
        const auto [add_result, add_overflow] {overflowing_add(a, b)};
        const auto [sub_result, sub_overflow] {overflowing_sub(a, b)};
        const auto [mul_result, mul_overflow] {overflowing_mul(a, b)};

        std::cout << "overflowing_add(100, 50) = " << add_result
                  << " (overflow: " << add_overflow << ")" << std::endl;
        std::cout << "overflowing_sub(100, 50) = " << sub_result
                  << " (overflow: " << sub_overflow << ")" << std::endl;
        std::cout << "overflowing_mul(100, 50) = " << mul_result
                  << " (overflow: " << mul_overflow << ")" << std::endl;
        // Output: 150/false, 50/false, 5000/false
    }

    // Using the overflow flag for conditional logic
    {
        const u32 a {1000000000U};
        const u32 b {5U};

        if (const auto [result, overflowed] {overflowing_mul(a, b)}; !overflowed)
        {
            std::cout << "Safe multiplication: " << a << " * " << b << " = " << result << std::endl;
        }
        else
        {
            std::cout << "Multiplication would overflow!" << std::endl;
        }
    }

    return 0;
}
//]

Output:

overflowing_add(max, 100):
  result = 99
  overflowed = true
overflowing_sub(10, 100):
  result = 4294967206
  underflowed = true
overflowing_mul(max, 2):
  result = 4294967294
  overflowed = true
overflowing_add(100, 50) = 150 (overflow: false)
overflowing_sub(100, 50) = 50 (overflow: false)
overflowing_mul(100, 50) = 5000 (overflow: false)
Safe multiplication: 1000000000 * 5 = 5000000000

Checked Arithmetic

Checked arithmetic returns std::optional - containing the result on success, or std::nullopt on overflow. This provides exception-free error handling with a clean, idiomatic interface.

Example 5. This example demonstrates checked arithmetic operations.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[checked_arithmetic_example
//` This example demonstrates the use of checked arithmetic operations.
//` These functions return std::optional - containing the result if the
//` operation succeeded, or std::nullopt if overflow/underflow occurred.
//` This provides a clean, exception-free way to handle arithmetic errors.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <optional>

int main()
{
    using boost::safe_numbers::u32;
    using boost::safe_numbers::checked_add;
    using boost::safe_numbers::checked_sub;
    using boost::safe_numbers::checked_mul;
    using boost::safe_numbers::checked_div;

    // Checked addition: returns nullopt on overflow
    {
        const u32 a {std::numeric_limits<std::uint32_t>::max()};
        const u32 b {100U};
        const auto result {checked_add(a, b)};

        if (result.has_value())
        {
            std::cout << "checked_add(max, 100) = " << *result << std::endl;
        }
        else
        {
            std::cout << "checked_add(max, 100) = overflow detected!" << std::endl;
        }
        // Output: overflow detected!
    }

    // Checked subtraction: returns nullopt on underflow
    {
        const u32 a {10U};
        const u32 b {100U};
        const auto result {checked_sub(a, b)};

        if (result.has_value())
        {
            std::cout << "checked_sub(10, 100) = " << *result << std::endl;
        }
        else
        {
            std::cout << "checked_sub(10, 100) = underflow detected!" << std::endl;
        }
        // Output: underflow detected!
    }

    // Checked division: returns nullopt on divide by zero
    {
        const u32 a {100U};
        const u32 b {0U};
        const auto result {checked_div(a, b)};

        if (result.has_value())
        {
            std::cout << "checked_div(100, 0) = " << *result << std::endl;
        }
        else
        {
            std::cout << "checked_div(100, 0) = division by zero!" << std::endl;
        }
        // Output: division by zero!
    }

    // Successful operations return the value wrapped in optional
    {
        const u32 a {100U};
        const u32 b {50U};

        // Using value_or for safe access with default
        std::cout << "checked_add(100, 50) = "
                  << checked_add(a, b).value_or(u32{0U}) << std::endl;
        std::cout << "checked_sub(100, 50) = "
                  << checked_sub(a, b).value_or(u32{0U}) << std::endl;
        std::cout << "checked_mul(100, 50) = "
                  << checked_mul(a, b).value_or(u32{0U}) << std::endl;
        // Output: 150, 50, 5000
    }

    // Chaining checked operations with value_or
    {
        const u32 a {1000000000U};
        const u32 b {5U};

        // Only proceed if multiplication doesn't overflow
        const auto product {checked_mul(a, b)};
        if (product)
        {
            std::cout << "Safe: " << a << " * " << b << " = " << *product << std::endl;
        }
        else
        {
            std::cout << "Operation would overflow, using fallback" << std::endl;
        }
    }

    return 0;
}
//]

Output:

checked_add(max, 100) = overflow detected!
checked_sub(10, 100) = underflow detected!
checked_div(100, 0) = division by zero!
checked_add(100, 50) = 150
checked_sub(100, 50) = 50
checked_mul(100, 50) = 5000
Safe: 1000000000 * 5 = 5000000000

Wrapping Arithmetic

Wrapping arithmetic performs standard C unsigned integer wrapping behavior - results wrap around modulo 2^N. This matches the behavior of built-in unsigned integers and is useful for implementing counters, checksums, or hash functions.

Example 6. This example demonstrates wrapping arithmetic operations.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[wrapping_arithmetic_example
//` This example demonstrates the use of wrapping arithmetic operations.
//` These functions perform standard C unsigned integer wrapping behavior -
//` when overflow or underflow occurs, the result wraps around modulo 2^N.
//` This matches the behavior of built-in unsigned integers in C/C++.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>

int main()
{
    using boost::safe_numbers::u8;
    using boost::safe_numbers::u32;
    using boost::safe_numbers::wrapping_add;
    using boost::safe_numbers::wrapping_sub;
    using boost::safe_numbers::wrapping_mul;

    // Wrapping addition: wraps around on overflow
    {
        const u8 a {255U};  // UINT8_MAX
        const u8 b {2U};
        const u8 result {wrapping_add(a, b)};

        std::cout << "wrapping_add(255, 2) = " << result << std::endl;
        // Output: 1 (255 + 2 = 257, wraps to 257 % 256 = 1)
    }

    // Wrapping subtraction: wraps around on underflow
    {
        const u8 a {0U};
        const u8 b {1U};
        const u8 result {wrapping_sub(a, b)};

        std::cout << "wrapping_sub(0, 1) = " << result << std::endl;
        // Output: 255 (wraps to UINT8_MAX)
    }

    // Wrapping multiplication: wraps around on overflow
    {
        const u8 a {200U};
        const u8 b {2U};
        const u8 result {wrapping_mul(a, b)};

        std::cout << "wrapping_mul(200, 2) = " << result << std::endl;
        // Output: 144 (200 * 2 = 400, wraps to 400 % 256 = 144)
    }

    // Demonstration with u32
    {
        const u32 max_val {std::numeric_limits<std::uint32_t>::max()};
        const u32 one {1U};

        std::cout << "wrapping_add(UINT32_MAX, 1) = "
                  << wrapping_add(max_val, one) << std::endl;
        // Output: 0 (wraps around)

        const u32 zero {0U};
        std::cout << "wrapping_sub(0, 1) = "
                  << wrapping_sub(zero, one) << std::endl;
        // Output: 4294967295 (UINT32_MAX)
    }

    // Normal operations that don't overflow work as expected
    {
        const u32 a {100U};
        const u32 b {50U};

        std::cout << "wrapping_add(100, 50) = " << wrapping_add(a, b) << std::endl;
        std::cout << "wrapping_sub(100, 50) = " << wrapping_sub(a, b) << std::endl;
        std::cout << "wrapping_mul(100, 50) = " << wrapping_mul(a, b) << std::endl;
        // Output: 150, 50, 5000
    }

    // Useful for implementing counters that wrap
    {
        u8 counter {254U};
        std::cout << "Counter sequence: ";
        for (int i = 0; i < 5; ++i)
        {
            std::cout << counter << " ";
            counter = wrapping_add(counter, u8{1U});
        }
        std::cout << std::endl;
        // Output: 254 255 0 1 2
    }

    return 0;
}
//]

Output:

wrapping_add(255, 2) = 1
wrapping_sub(0, 1) = 255
wrapping_mul(200, 2) = 144
wrapping_add(UINT32_MAX, 1) = 0
wrapping_sub(0, 1) = 4294967295
wrapping_add(100, 50) = 150
wrapping_sub(100, 50) = 50
wrapping_mul(100, 50) = 5000
Counter sequence: 254 255 0 1 2

Strict Arithmetic

Strict arithmetic calls std::exit(EXIT_FAILURE) on overflow, underflow, or division by zero. This provides a hard termination policy for safety-critical applications where exceptions cannot be used but silent wrapping is unacceptable. All strict functions are noexcept.

Example 7. This example demonstrates strict arithmetic operations.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[strict_arithmetic_example
//` This example demonstrates the use of strict arithmetic operations.
//` These functions call std::exit(EXIT_FAILURE) on overflow, underflow,
//` or division by zero. They are designed for safety-critical applications
//` where exceptions cannot be used but silent wrapping is unacceptable.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>

int main()
{
    using boost::safe_numbers::u32;
    using boost::safe_numbers::strict_add;
    using boost::safe_numbers::strict_sub;
    using boost::safe_numbers::strict_mul;
    using boost::safe_numbers::strict_div;
    using boost::safe_numbers::strict_mod;

    // Normal operations that don't overflow work as expected
    {
        const u32 a {100U};
        const u32 b {50U};

        std::cout << "strict_add(100, 50) = " << strict_add(a, b) << std::endl;
        std::cout << "strict_sub(100, 50) = " << strict_sub(a, b) << std::endl;
        std::cout << "strict_mul(100, 50) = " << strict_mul(a, b) << std::endl;
        std::cout << "strict_div(100, 50) = " << strict_div(a, b) << std::endl;
        std::cout << "strict_mod(100, 50) = " << strict_mod(a, b) << std::endl;
    }

    // Strict arithmetic is noexcept - safe for -fno-exceptions environments
    {
        const u32 a {1000000U};
        const u32 b {3U};

        // These are guaranteed to never throw
        static_assert(noexcept(strict_add(a, b)));
        static_assert(noexcept(strict_sub(a, b)));
        static_assert(noexcept(strict_mul(a, b)));
        static_assert(noexcept(strict_div(a, b)));
        static_assert(noexcept(strict_mod(a, b)));

        std::cout << "strict_div(1000000, 3) = " << strict_div(a, b) << std::endl;
        std::cout << "strict_mod(1000000, 3) = " << strict_mod(a, b) << std::endl;
    }

    // NOTE: If any of these operations would overflow, underflow, or
    // divide by zero, the program would immediately terminate via
    // std::exit(EXIT_FAILURE). For example:
    //
    //   u32 max_val {std::numeric_limits<std::uint32_t>::max()};
    //   strict_add(max_val, u32{1U});  // Terminates the program
    //
    //   strict_sub(u32{0U}, u32{1U});  // Terminates the program
    //
    //   strict_div(u32{1U}, u32{0U});  // Terminates the program

    return 0;
}
//]

Output:

strict_add(100, 50) = 150
strict_sub(100, 50) = 50
strict_mul(100, 50) = 5000
strict_div(100, 50) = 2
strict_mod(100, 50) = 0
strict_div(1000000, 3) = 333333
strict_mod(1000000, 3) = 1

Generic Policy-Parameterized Arithmetic

The generic add, sub, mul, div, and mod functions accept an overflow_policy as a template parameter, allowing you to write code that is generic over the overflow handling strategy. The return type varies by policy.

Example 8. This example demonstrates the generic policy-parameterized interface.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[generic_arithmetic_example
//` This example demonstrates the generic policy-parameterized arithmetic
//` functions (add, sub, mul, div, mod). These accept an overflow_policy
//` as a template parameter, allowing you to write code that is generic
//` over the overflow handling strategy.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/overflow_policy.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>

int main()
{
    using boost::safe_numbers::u32;
    using boost::safe_numbers::overflow_policy;
    using boost::safe_numbers::add;
    using boost::safe_numbers::sub;
    using boost::safe_numbers::mul;

    const u32 a {100U};
    const u32 b {50U};

    // Same operation, different policies via the generic interface
    {
        const auto throwing  {add<overflow_policy::throw_exception>(a, b)};
        const auto saturated {add<overflow_policy::saturate>(a, b)};
        const auto wrapped   {add<overflow_policy::wrapping>(a, b)};
        const auto strict    {add<overflow_policy::strict>(a, b)};

        std::cout << "add<throw_exception>(100, 50) = " << throwing << std::endl;
        std::cout << "add<saturate>(100, 50)        = " << saturated << std::endl;
        std::cout << "add<wrapping>(100, 50)        = " << wrapped << std::endl;
        std::cout << "add<strict>(100, 50)          = " << strict << std::endl;
    }

    // Policies with different return types
    {
        // overflow_tuple returns std::pair<T, bool>
        const auto [result_ot, overflowed] {add<overflow_policy::overflow_tuple>(a, b)};
        std::cout << "add<overflow_tuple>(100, 50)   = " << result_ot
                  << " (overflowed: " << std::boolalpha << overflowed << ")" << std::endl;

        // checked returns std::optional<T>
        const auto result_chk {add<overflow_policy::checked>(a, b)};
        if (result_chk)
        {
            std::cout << "add<checked>(100, 50)          = " << *result_chk << std::endl;
        }
    }

    // The real power: writing generic algorithms parameterized on policy
    {
        const u32 max_val {std::numeric_limits<std::uint32_t>::max()};
        const u32 one {1U};

        // checked policy returns nullopt on overflow
        const auto checked_result {add<overflow_policy::checked>(max_val, one)};
        std::cout << "add<checked>(max, 1)           = "
                  << (checked_result ? "has value" : "nullopt (overflow)")
                  << std::endl;

        // saturate policy clamps to max
        const auto sat_result {add<overflow_policy::saturate>(max_val, one)};
        std::cout << "add<saturate>(max, 1)          = " << sat_result << std::endl;

        // wrapping policy wraps around
        const auto wrap_result {add<overflow_policy::wrapping>(max_val, one)};
        std::cout << "add<wrapping>(max, 1)          = " << wrap_result << std::endl;
    }

    return 0;
}
//]

Output:

add<throw_exception>(100, 50) = 150
add<saturate>(100, 50)        = 150
add<wrapping>(100, 50)        = 150
add<strict>(100, 50)          = 150
add<overflow_tuple>(100, 50)   = 150 (overflowed: false)
add<checked>(100, 50)          = 150
add<checked>(max, 1)           = nullopt (overflow)
add<saturate>(max, 1)          = 4294967295
add<wrapping>(max, 1)          = 0

Literals

User-defined literal suffixes (_u8, _u16, _u32, _u64, _u128) provide concise construction of safe integer types. Values are range-checked at construction, producing a compile error in constexpr contexts or throwing std::overflow_error at runtime for out-of-range values.

Example 9. This example demonstrates how to use user-defined literals with safe integer types.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[literals_example
//` This example demonstrates the use of user-defined literals for
//` constructing safe integer types. The literals provide a concise
//` syntax and perform compile-time range checking when possible.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/literals.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>

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

    // Construct safe integers using literal suffixes
    {
        constexpr auto a {42_u8};
        constexpr auto b {1000_u16};
        constexpr auto c {100000_u32};
        constexpr auto d {9999999999_u64};
        constexpr auto e {340282366920938463463374607431768211455_u128};

        std::cout << "42_u8   = " << a << std::endl;
        std::cout << "1000_u16 = " << b << std::endl;
        std::cout << "100000_u32 = " << c << std::endl;
        std::cout << "9999999999_u64 = " << d << std::endl;
        std::cout << "max_u128 = " << e << std::endl;
    }

    // Literals work naturally in expressions
    {
        const auto sum {100_u32 + 50_u32};
        const auto product {6_u32 * 7_u32};

        std::cout << "100_u32 + 50_u32 = " << sum << std::endl;
        std::cout << "6_u32 * 7_u32    = " << product << std::endl;
    }

    // Literals are constexpr - can be used in compile-time contexts
    {
        constexpr auto compile_time {255_u8};
        static_assert(compile_time == u8{255U});

        constexpr auto zero {0_u32};
        static_assert(zero == u32{0U});

        std::cout << "constexpr 255_u8 = " << compile_time << std::endl;
    }

    // NOTE: Out-of-range literals throw std::overflow_error at runtime,
    // or produce a compile error when used in constexpr context:
    //
    //   auto bad = 256_u8;    // throws std::overflow_error (> UINT8_MAX)
    //   auto bad = 70000_u16; // throws std::overflow_error (> UINT16_MAX)

    return 0;
}
//]

Output:

42_u8   = 42
1000_u16 = 1000
100000_u32 = 100000
9999999999_u64 = 9999999999
max_u128 = 340282366920938463463374607431768211455
100_u32 + 50_u32 = 150
6_u32 * 7_u32    = 42
constexpr 255_u8 = 255

Character Conversion

The library provides to_chars and from_chars functions for converting between safe integers and strings using Boost.Charconv.

Example 10. This example demonstrates character conversion with various bases.
// 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/unsigned_integers.hpp>
#include <boost/safe_numbers/charconv.hpp>
#include <iostream>
#include <cstring>

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

    // to_chars: Convert safe integer to string
    u32 value {12345};
    char buffer[32];

    auto result = to_chars(buffer, buffer + sizeof(buffer), value);
    if (result)
    {
        *result.ptr = '\0';  // Null-terminate
        std::cout << "to_chars (base 10): " << buffer << '\n';
    }

    // to_chars with different bases
    result = to_chars(buffer, buffer + sizeof(buffer), value, 16);
    if (result)
    {
        *result.ptr = '\0';
        std::cout << "to_chars (base 16): " << buffer << '\n';
    }

    result = to_chars(buffer, buffer + sizeof(buffer), value, 2);
    if (result)
    {
        *result.ptr = '\0';
        std::cout << "to_chars (base 2):  " << buffer << '\n';
    }

    std::cout << '\n';

    // from_chars: Convert string to safe integer
    const char* str = "98765";
    u32 parsed_value {};

    auto parse_result = from_chars(str, str + std::strlen(str), parsed_value);
    if (parse_result)
    {
        std::cout << "from_chars (base 10): " << static_cast<std::uint32_t>(parsed_value) << '\n';
    }

    // from_chars with hexadecimal
    const char* hex_str = "1a2b";
    u32 hex_value {};

    parse_result = from_chars(hex_str, hex_str + std::strlen(hex_str), hex_value, 16);
    if (parse_result)
    {
        std::cout << "from_chars (base 16): " << static_cast<std::uint32_t>(hex_value) << '\n';
    }

    // from_chars with binary
    const char* bin_str = "11010";
    u8 bin_value {};

    parse_result = from_chars(bin_str, bin_str + std::strlen(bin_str), bin_value, 2);
    if (parse_result)
    {
        std::cout << "from_chars (base 2):  " << static_cast<unsigned>(bin_value) << '\n';
    }

    return 0;
}

Output:

to_chars (base 10): 12345
to_chars (base 16): 3039
to_chars (base 2):  11000000111001

from_chars (base 10): 98765
from_chars (base 16): 6699
from_chars (base 2):  26

Formatting with {fmt}

The library supports formatting with both <format> (C++20) and <fmt/format.h>. All standard integer format specifiers are supported.

The header <boost/safe_numbers/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency.
Example 11. This example demonstrates formatting with {fmt}.
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#define FMT_HEADER_ONLY

#if __has_include(<fmt/format.h>)

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/fmt_format.hpp>
#include <fmt/format.h>
#include <iostream>

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

    const u32 val1 {12345};
    const u64 val2 {9876543210};

    // Default format (decimal)
    std::cout << "Default Format:\n";
    std::cout << fmt::format("{}", val1) << '\n';
    std::cout << fmt::format("{}", val2) << "\n\n";

    // Hexadecimal format
    std::cout << "Hexadecimal Format:\n";
    std::cout << fmt::format("{:x}", val1) << '\n';
    std::cout << fmt::format("{:#x}", val2) << "\n\n";

    // Binary format
    std::cout << "Binary Format:\n";
    std::cout << fmt::format("{:b}", val1) << '\n';
    std::cout << fmt::format("{:#b}", u8{42}) << "\n\n";

    // Octal format
    std::cout << "Octal Format:\n";
    std::cout << fmt::format("{:o}", val1) << '\n';
    std::cout << fmt::format("{:#o}", val1) << "\n\n";

    // Padding and alignment
    std::cout << "Padding and Alignment:\n";
    std::cout << fmt::format("{:>10}", val1) << '\n';   // Right align
    std::cout << fmt::format("{:<10}", val1) << '\n';   // Left align
    std::cout << fmt::format("{:^10}", val1) << '\n';   // Center align
    std::cout << fmt::format("{:0>10}", val1) << "\n\n"; // Zero-padded

    // Fill character
    std::cout << "Fill Character:\n";
    std::cout << fmt::format("{:*>10}", val1) << '\n';
    std::cout << fmt::format("{:_<10}", val1) << '\n';

    return 0;
}

#else

#include <iostream>

int main()
{
    std::cout << "{fmt} headers are required to run this example." << std::endl;
}

#endif

Output:

Default Format:
12345
9876543210

Hexadecimal Format:
3039
0x24cb016ea

Binary Format:
11000000111001
0b101010

Octal Format:
30071
030071

Padding and Alignment:
     12345
12345
  12345
0000012345

Fill Character:
*****12345
12345_____

Stream I/O

The library provides operator<< and operator>> overloads for all safe integer types. The u8 type is handled specially: it displays as a numeric value rather than as a character.

Example 12. This example demonstrates stream I/O with safe integer types.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

//[iostream_example
//` This example demonstrates stream I/O support for safe integer types.
//` Safe integers can be written to and read from standard streams using
//` the familiar << and >> operators. The u8 type displays as a number
//` rather than as a character.

#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint>

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

    // Output: safe integers work with std::cout
    {
        const u8  a {42U};
        const u16 b {1000U};
        const u32 c {100000U};
        const u64 d {9999999999ULL};

        std::cout << "u8:  " << a << std::endl;
        std::cout << "u16: " << b << std::endl;
        std::cout << "u32: " << c << std::endl;
        std::cout << "u64: " << d << std::endl;
    }

    // u8 displays as a number, not a character
    {
        const u8 newline {10U};
        const u8 space {32U};

        std::cout << "u8(10) = " << newline << " (not a newline character)" << std::endl;
        std::cout << "u8(32) = " << space << " (not a space character)" << std::endl;
    }

    // Input: safe integers work with std::istringstream
    {
        u32 value;
        std::istringstream input {"12345"};
        input >> value;

        std::cout << "Read from stream: " << value << std::endl;
    }

    // Works with stream formatting
    {
        const u32 val {255U};
        std::cout << "Decimal:     " << std::dec << val << std::endl;
        std::cout << "Hexadecimal: " << std::hex << val << std::endl;
        std::cout << "Octal:       " << std::oct << val << std::endl;
        std::cout << std::dec; // Reset to decimal
    }

    // Roundtrip through a stringstream
    {
        const u64 original {18446744073709551615ULL};
        std::stringstream ss;
        ss << original;

        u64 restored;
        ss >> restored;

        std::cout << "Roundtrip: " << original << " -> \"" << original << "\" -> " << restored << std::endl;
    }

    return 0;
}
//]

Output:

u8:  42
u16: 1000
u32: 100000
u64: 9999999999
u8(10) = 10 (not a newline character)
u8(32) = 32 (not a space character)
Read from stream: 12345
Decimal:     255
Hexadecimal: ff
Octal:       377
Roundtrip: 18446744073709551615 -> "18446744073709551615" -> 18446744073709551615

Policy Comparison

The following table summarizes the behavior of each arithmetic policy:

Policy On Overflow Return Type Use Case

Default (operators)

Throws std::overflow_error

T

When overflow is a programming error

Saturating

Clamps to min/max

T

Bounded values, DSP, graphics

Overflowing

Returns wrapped value + flag

std::pair<T, bool>

Need both wrapped value and detection

Checked

Returns std::nullopt

std::optional<T>

Exception-free error handling

Wrapping

Wraps around (modulo 2^N)

T

Counters, checksums, hashing

Strict

Calls std::exit(EXIT_FAILURE)

T

Safety-critical, no-exceptions environments

Generic (add<Policy>)

Depends on policy

Depends on policy

Policy-generic algorithms