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.

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 <unsigned_integral BasisType>
struct std::formatter<boost::safe_numbers::detail::unsigned_integer_basis<BasisType>>
    : std::formatter<BasisType>
{
    auto format(const unsigned_integer_basis<BasisType>& val,
                std::format_context& ctx) const;
};

// <boost/safe_numbers/fmt_format.hpp>
template <unsigned_integral BasisType>
struct fmt::formatter<boost::safe_numbers::detail::unsigned_integer_basis<BasisType>>
    : fmt::formatter<BasisType>
{
    auto format(const unsigned_integer_basis<BasisType>& 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/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_____

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