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

Character Conversion

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

Example 7. 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 8. 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_____

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