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 type’s formatter, so all standard format specifiers for that type are supported. Formatting is available for every safe number type: the safe integers (u8, u16, u32, u64, u128, i8, i16, i32, i64, i128), the safe floating-point types (f32, f64), and the bounded types (bounded_uint<Min, Max>, bounded_int<Min, Max>, bounded_float<Min, Max>). The integer format specifiers documented below apply to the integer types; for f32 and f64 the standard floating-point specifiers are forwarded to the underlying float or double formatter, as described in Floating-Point Format Specifiers.

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"

Floating-Point Format Specifiers

For f32 and f64 the formatter forwards to the underlying float or double formatter, so the standard floating-point presentation types and precision apply instead of the integer type modifiers above.

Presentation Meaning

(default)

Shortest representation that round-trips to the same value

f

Fixed-point notation

e

Scientific notation

g

General: fixed or scientific, whichever is shorter

a

Hexadecimal floating-point notation

A precision is written after a dot: {:.2f} formats f64{3.14159} as 3.14. The fill, alignment, width, sign, and locale specifiers described above apply to the floating-point types as well.

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.