Formatting Support

Description

Boost.SafeNumbers supports formatting with both <format> (C++20 and later) and <fmt/format.h> (all language standards). The formatters delegate to the underlying integer type’s formatter, so all standard integer format specifiers are supported. Both safe unsigned integers (u8, u16, u32, u64, u128) and bounded integers (bounded_uint<Min, Max>) are supported.

std::format is supported when using C++20 or later with a compiler that has appropriate support: GCC >= 13, Clang >= 18, MSVC >= 19.40.

#include <boost/safe_numbers/format.hpp>     // For std::format support
#include <boost/safe_numbers/fmt_format.hpp> // For fmt::format support

Synopsis

// <boost/safe_numbers/format.hpp>
template <detail::library_type T>
struct std::formatter<T>
    : std::formatter<detail::underlying_type_t<T>>
{
    auto format(const T& val, std::format_context& ctx) const;
};

// <boost/safe_numbers/fmt_format.hpp>
template <detail::library_type T>
struct fmt::formatter<T>
    : fmt::formatter<detail::underlying_type_t<T>>
{
    auto format(const T& val, fmt::format_context& ctx) const;
};

Type Modifiers

The following type modifiers are supported (same as built-in integer types):

Modifier Format Example

d

Decimal (default)

12345

x

Lowercase hexadecimal

3039

X

Uppercase hexadecimal

3039

o

Octal

30071

b

Lowercase binary

11000000111001

B

Uppercase binary

11000000111001

Example usage for hexadecimal format: {:x}

Alternate Form

Use # to enable alternate form which adds a prefix:

Format Prefix

{:#x}

0x

{:#X}

0X

{:#o}

0

{:#b}

0b

{:#B}

0B

Padding and Alignment

Values can be padded to a fixed width with optional alignment:

Specifier Alignment Example (val = 42)

{:>10}

Right (default)

` 42`

{:<10}

Left

`42 `

{:^10}

Center

` 42 `

{:0>10}

Right with zero fill

0000000042

Fill Character

A custom fill character can be specified before the alignment:

  • {:*>10} produces **42

  • {:_<10} produces 42__

Sign Modifier

For unsigned types, sign modifiers have limited effect since values are always non-negative:

Modifier Effect

+

Always show sign (shows + for positive values)

-

Only show sign for negative (no effect for unsigned)

` `

Space for positive, minus for negative

Locale Modifier

Use L to apply locale-specific formatting (e.g., thousand separators):

std::locale::global(std::locale("en_US.UTF-8"));
std::cout << std::format("{:L}", u32{1234567}); // "1,234,567"

Format Specifier Order

The full format specifier order is:

{[fill][align][sign][#][0][width][.precision][L][type]}

Examples

The header <boost/safe_numbers/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency on a potentially compiled library.
Example 1. This example demonstrates how to use {fmt} with the safe integer types.
// 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/bounded_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\n";

    // Bounded integer types
    using percent = bounded_uint<0u, 100u>;
    using port = bounded_uint<1u, 65535u>;

    std::cout << "Bounded Integers:\n";
    std::cout << fmt::format("{}", percent{75u}) << '\n';
    std::cout << fmt::format("{:>8}", port{8080u}) << '\n';
    std::cout << fmt::format("{:#x}", port{443u}) << '\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_____

Bounded Integers:
75
    8080
0x1bb

This same example can be run with <format> by replacing fmt::format with std::format and including <boost/safe_numbers/format.hpp> instead.