How fmtlib Implements 128-Bit Integer Support in format.h

fmtlib provides portable 128-bit integer formatting by detecting compiler-native __int128 types and falling back to a custom uint128 class in include/fmt/format.h when necessary.

fmtlib is a modern C++ formatting library that serves as a fast, type-safe alternative to printf and iostreams. The library's implementation of 128-bit integer support ensures seamless formatting of extremely large values across GCC, Clang, and MSVC compilers. This deep dive examines how the format.h header coordinates with base.h and format-inl.h to provide robust extended-precision integer handling.

Detecting Native 128-Bit Integer Types

The implementation begins in include/fmt/base.h by checking whether the compiler supplies built-in 128-bit integer support. When the compiler defines __SIZEOF_INT128__, the library sets FMT_USE_INT128 to 1 and creates aliases for the native types:

  • native_int128 maps to __int128_t (line 412)
  • native_uint128 maps to __uint128_t (line 413)

These aliases allow the library to reference compiler-specific extended integer types through portable names. On GCC and Clang, these map directly to the compiler's built-in types, while MSVC and other compilers without native support trigger the fallback path.

Selecting the Concrete Implementation

In include/fmt/format.h at line 394, the public-facing type is selected using a conditional type alias:

using uint128_t = conditional_t<FMT_USE_INT128, native_uint128, uint128>;

If FMT_USE_INT128 evaluates to true, uint128_t becomes an alias for the compiler's native __uint128_t. Otherwise, the header instantiates the custom uint128 class beginning at line 287. This abstraction ensures that user code remains identical regardless of the underlying platform capabilities.

The Fallback uint128 Class

When native support is unavailable, fmtlib provides a minimal but complete implementation of a 128-bit unsigned integer. The uint128 class defined in format.h supplies exactly the operations required for formatting algorithms without unnecessary overhead.

Construction and Conversion

The fallback class provides constructors for building 128-bit values from high and low 64-bit components or single 64-bit values:

constexpr uint128(uint64_t hi, uint64_t lo);
constexpr uint128(uint64_t value = 0);

These appear at lines 292-293. The class also defines explicit conversion operators (lines 298-301) that allow safe narrowing to smaller integral types when needed for internal calculations.

Arithmetic and Bitwise Operations

To support the formatting pipeline, the class implements a focused set of operators:

  • Comparison operators (==, !=, >) defined as constexpr friends at lines 303-315
  • Bitwise operators (|, &, ~) implemented as constexpr friends at lines 315-323
  • Basic arithmetic (+, -, *, shifts) at lines 326-353
  • Compound assignment (+=, &=) including optimized paths using compiler intrinsics at lines 356-364

A specialized fast path for incrementing by 64-bit values appears at lines 668-690, utilizing built-in intrinsics when available to accelerate the decimal conversion loop.

Integrating 128-Bit Types into Formatting

The formatting pipeline uses a width-selection strategy to choose the smallest sufficient integer type for any given value.

Integer Width Selection

At lines 559-566, format.h defines a type selector that maps any integer type to the optimal storage width:

template <typename T>
using uint32_or_64_or_128_t =
    conditional_t<num_bits<T>() <= 32 && !FMT_REDUCE_INT_INSTANTIATIONS,
                  uint32_t,
                  conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>>;

This template ensures that 128-bit operations are only used when necessary, reducing code bloat and improving performance for smaller integer types.

Counting Decimal Digits

For digit counting, format.h provides a specialization of count_digits for native_uint128 at lines 1254-1265:

FMT_CONSTEXPR20 inline auto count_digits(native_uint128 n) -> int { … }

When the fallback uint128 is active, the generic digit-counting logic operates correctly because the class implements the requisite arithmetic and comparison operators.

Decimal Conversion

The core formatting routine format_decimal (lines 1444-1448) receives uint128_t values and processes them through do_format_decimal, which extracts two-digit groups iteratively. Since the fallback class implements division and modulo operations, the same algorithm handles both native and emulated 128-bit types without modification.

Multiplication Support in format-inl.h

Complex formatting operations, particularly within the Dragon4 floating-point algorithm, require high-precision multiplication. The implementation in include/fmt/format-inl.h provides helper functions operating on uint128 values:

inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128 { … }

Located at lines 1598-1600, umul128 computes the full 128-bit product of two 64-bit integers. Additional helpers like umul192_upper128 support wider multiplications needed for precise floating-point formatting, treating 128-bit integers as first-class arithmetic types throughout the library.

Practical Usage Examples

Users interact with 128-bit support through the fmt::uint128_t type, which works identically on all supported compilers:

#include <fmt/core.h>
#include <fmt/format.h>

int main() {
    // Construct a 128-bit value (2^100)
    fmt::uint128_t big = static_cast<fmt::uint128_t>(1) << 100;
    
    // Decimal formatting
    std::string decimal = fmt::format("Value: {}", big);
    // Result: "Value: 1267650600228229401496703205376"
    
    // Hexadecimal formatting
    std::string hex = fmt::format("{:x}", big);
    // Result: "100000000000000000000"
    
    // Using high/low constructor on fallback implementations
    fmt::uint128_t composed = fmt::uint128_t{0x1, 0xFFFFFFFFFFFFFFFFULL};
    std::string formatted = fmt::format("{:d}", composed);
}

This code compiles on GCC 12, Clang 17, and MSVC, automatically utilizing native __uint128_t on the former two and the fallback uint128 class on the latter.

Summary

  • fmtlib detects native 128-bit support via __SIZEOF_INT128__ in include/fmt/base.h, defining native_int128 and native_uint128 aliases when available.
  • The public uint128_t type in include/fmt/format.h conditionally selects between native types and a fallback implementation starting at line 287.
  • The fallback uint128 class provides constexpr construction, comparison, bitwise, and arithmetic operators sufficient for formatting algorithms.
  • Width selection uses uint32_or_64_or_128_t to optimize for the smallest capable integer type, minimizing template instantiations.
  • Digit counting and decimal formatting work through count_digits and format_decimal specializations that handle both native and emulated 128-bit values uniformly.
  • Low-level multiplication support in include/fmt/format-inl.h enables high-precision floating-point formatting via umul128 and related helpers.

Frequently Asked Questions

Does fmtlib support 128-bit integers on all compilers?

Yes. On GCC and Clang, fmtlib uses the native __int128 and __uint128_t types. On MSVC and compilers without built-in 128-bit support, the library automatically falls back to the uint128 class defined in include/fmt/format.h, ensuring portable functionality across all major platforms.

What is the performance difference between native and fallback 128-bit types?

Native compiler types generally perform better for complex arithmetic, but the fallback uint128 implementation is optimized for the specific operations needed by formatting code. The class uses compiler intrinsics where available (lines 668-690) and provides only the minimal interface required, making the performance gap negligible for formatting workloads.

How do I format a 128-bit integer in hexadecimal?

Use the standard format specifier with x or X just as you would for smaller integers: fmt::format("{:x}", value). The uint128_t type supports all standard integer format specifiers including decimal (d), hexadecimal (x), and binary (b), with the underlying implementation in format_decimal handling the conversion regardless of whether native or fallback types are active.

Can I perform arithmetic operations directly on fmt::uint128_t?

Yes, but with limitations on non-native implementations. The fallback uint128 class supports essential operations including addition, subtraction, multiplication, bitwise AND/OR, and shifts. However, it is intentionally minimal—designed specifically for formatting rather than general-purpose arithmetic. For complex mathematical operations beyond formatting, consider using a dedicated multiprecision library like Boost.Multiprecision.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →