# Common Formatting Specifiers in fmtlib: A Complete Guide to Type-Safe C++ Formatting

> Explore common fmtlib formatting specifiers for type-safe C++ output. Master decimal, hex, float, string, and pointer formatting with alignment, width, and precision controls.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: deep-dive
- Published: 2026-09-06

---

**The most common formatting specifiers in fmtlib are `d`/`i` (signed decimal), `u` (unsigned), `x`/`X` (hexadecimal), `b` (binary), `o` (octal), `f`/`F` (fixed float), `e`/`E` (scientific), `g`/`G` (general), `s` (string), `p` (pointer), and `c` (character), combined with alignment (`<`/`>`/`^`), width, precision, and sign controls.**

The **{fmt}** library (commonly called **fmtlib**) provides a fast, type-safe alternative to C's `printf` and C++ streams. Its formatting syntax closely follows Python's `str.format` while adding C++-specific optimizations. This guide covers the core format specifiers defined in [`include/fmt/base.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/base.h) and demonstrates their practical usage.

## Core Format Specifiers for Integral Types

Integral types—`int`, `unsigned`, `bool`, `char`, and their variants—support multiple base representations. These specifiers are parsed in [`include/fmt/base.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/base.h) around line 655.

| Specifier | Meaning |
|-----------|---------|
| `d`, `i` | Signed decimal integer |
| `u` | Unsigned decimal integer |
| `b` | Binary (base 2) |
| `o` | Octal (base 8) |
| `x` | Lowercase hexadecimal |
| `X` | Uppercase hexadecimal |
| `c` | Character (converts integral to `char`) |

```cpp
#include <fmt/format.h>

// Base conversions
fmt::print("Decimal: {}, Hex: {:x}, Binary: {:b}\n", 255, 255, 255);
// Output: Decimal: 255, Hex: ff, Binary: 11111111

// Uppercase hex and octal
fmt::print("Upper hex: {:X}, Octal: {:o}\n", 255, 64);
// Output: Upper hex: FF, Octal: 100

// Character conversion
fmt::print("ASCII 65 = {:c}\n", 65);  // Output: ASCII 65 = A

```

## Floating-Point Format Specifiers

Floating-point formatting in fmtlib provides precise control over notation and precision. The specifiers mirror standard C conventions with extended functionality.

| Specifier | Meaning |
|-----------|---------|
| `f`, `F` | Fixed-point notation (e.g., `123.456`) |
| `e`, `E` | Scientific notation (e.g., `1.234560e+02`) |
| `g`, `G` | General format—uses `e` or `f` based on magnitude |
| `a`, `A` | Hexadecimal floating-point |
| `.N` | Precision: digits after decimal (or total digits for `g`) |

```cpp
double pi = 3.141592653589793;

// Fixed precision
fmt::print("{:.2f}\n", pi);     // 3.14

// Scientific notation
fmt::print("{:e}\n", pi);       // 3.141593e+00

// General format auto-selects
fmt::print("{:g}\n", 1000000.0);  // 1e+06
fmt::print("{:g}\n", 0.0001);     // 0.0001

// Hexadecimal float
fmt::print("{:a}\n", pi);       // 0x1.921fb54442d18p+1

```

## String and Pointer Specifiers

Strings and pointers have dedicated specifiers with special behavior for width and precision.

| Specifier | Meaning |
|-----------|---------|
| `s` | String (default for `std::string`, `char*`, `string_view`) |
| `p` | Pointer address (implementation-defined format) |
| `?` | Debug representation (calls custom formatter) |

```cpp
const char* cstr = "hello";
std::string cppstr = "world";
int value = 42;

// Basic string formatting
fmt::print("{:s} {:s}\n", cstr, cppstr);  // hello world

// Pointer address
fmt::print("{:p}\n", &value);  // 0x7ffd3e8c2a4c (example)

// Precision truncates strings
fmt::print("{:.3}\n", "substring");  // sub

```

## Alignment, Width, and Fill Options

The format specification mini-language supports flexible layout control through alignment and width specifiers.

| Component | Syntax | Meaning |
|-----------|--------|---------|
| Fill character | Any character before alignment | Pad with this character |
| Alignment | `<` (left), `>` (right), `^` (center) | Position within field |
| Width | Number or `*` | Minimum field width |
| Precision | `.N` or `.*` | Digits after decimal or max string length |

```cpp
// Right-aligned (default for numbers)
fmt::print("[{:>10}]\n", 42);     // [        42]

// Left-aligned (default for strings)
fmt::print("[{:<10}]\n", "hi");   // [hi        ]

// Centered
fmt::print("[{:^10}]\n", "mid");  // [   mid    ]

// Custom fill character
fmt::print("[{:*>10}]\n", 42);    // [********42]

// Combined: zero-padding for numbers
fmt::print("[{:08}]\n", 42);      // [00000042]

```

## Sign, Hash, and Locale Modifiers

Additional flags modify numeric presentation for specialized output requirements.

| Flag | Effect |
|------|--------|
| `+` | Always show sign (`+42`, `-7`) |
| `-` | Only negative sign (default) |
| ` ` (space) | Space for positive, minus for negative |
| `#` | Alternate form: `0x` prefix for hex, `0` for octal, decimal point for floats |
| `L` | Use locale-specific thousands separator |

```cpp
// Sign control
fmt::print("{:+d} {:+d}\n", 42, -7);   // +42 -7
fmt::print("{: d} {: d}\n", 42, -7);   //  42 -7

// Alternate form
fmt::print("{:#x}\n", 255);            // 0xff
fmt::print("{:#o}\n", 8);              // 010
fmt::print("{:#.0f}\n", 1000.0);       // 1000.

// Locale-aware formatting (requires locale support)
fmt::print("{:L}\n", 1234567);         // 1,234,567 (with appropriate locale)

```

## Dynamic Width and Precision

Fmtlib supports runtime-specified widths and precisions using `*` substitution, evaluated from subsequent arguments.

```cpp
int width = 10;
int precision = 3;
double value = 3.14159;

// Dynamic width
fmt::print("[{:{}}]\n", 42, width);           // [        42]

// Dynamic precision
fmt::print("[{:.{}}]\n", value, precision);   // [3.142]

// Both dynamic
fmt::print("[{:{}}]\n", value, width);        // 3.14159 (no precision specified)

// Common pattern: table alignment
std::vector<std::pair<std::string, int>> data = {
    {"Alice", 1500},
    {"Bob", 25000},
    {"Charlie", 300}
};

int col_width = 10;
for (auto& [name, score] : data) {
    fmt::print("{:<{}} | {:>{}}\n", name, col_width, score, col_width);
}
// Alice      |       1500
// Bob        |      25000
// Charlie    |        300

```

## Range Formatting with Custom Specifiers

The [`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h) header extends specifier support to containers and ranges through `fmt::join` and automatic range formatting.

```cpp
#include <fmt/ranges.h>
#include <vector>
#include <map>

std::vector<int> nums = {1, 2, 3, 4};

// Default range formatting
fmt::print("{}\n", nums);  // [1, 2, 3, 4]

// Custom element formatting via join
fmt::print("{}\n", fmt::join(nums, " | "));  // 1 | 2 | 3 | 4

// Specifier applied to each element
fmt::print("{::04x}\n", nums);  // [0x0001, 0x0002, 0x0003, 0x0004]

// Nested containers
std::vector<std::vector<int>> nested = {{1, 2}, {3, 4}};
fmt::print("{}\n", nested);  // [[1, 2], [3, 4]]

```

## Complete Format Specification Syntax

The full grammar for a replacement field in [`include/fmt/base.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/base.h) follows this structure:

```

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

```

Where:
- `arg_id` — explicit argument index (e.g., `{0}`, `{1}`)
- `fill` — any character except `{` or `}`
- `align` — `<` (left), `>` (right), `^` (center), `=` (numeric, pad after sign)
- `sign` — `+`, `-`, or ` ` (space)
- `#` — alternate form flag
- `0` — zero-fill (equivalent to fill=`0`, align=`=`)
- `width` — integer or `*` for dynamic
- `precision` — `.` followed by integer or `*` for dynamic
- `type` — format specifier from tables above

```cpp
// Complex specification combining multiple features
fmt::print("{:+#08.2f}\n", 3.14159);  // +003.14

// Breakdown: +: always show sign
//            #: alternate form (ensures decimal point)
//            0: zero-pad
//            8: total width
//            .2: 2 digits after decimal
//            f: fixed-point

```

## Summary

- **Integral specifiers**: `d`/`i`, `u`, `b`, `o`, `x`/`X`, `c` control base and character conversion
- **Floating-point specifiers**: `f`/`F`, `e`/`E`, `g`/`G`, `a`/`A` select notation; precision controls digits
- **Layout controls**: `<`/`>`/`^` alignment, custom fill, `width`, and `.precision` manage field sizing
- **Numeric flags**: `+`/`-`/` ` for signs, `#` for alternate forms, `0` for zero-padding, `L` for locale
- **Dynamic values**: `*` in width or precision position enables runtime specification
- **Range support**: [`include/fmt/ranges.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/ranges.h) extends specifiers to containers with `fmt::join` and `::` syntax

## Frequently Asked Questions

### What is the difference between `d` and `i` specifiers in fmtlib?

Both `d` and `i` produce identical signed decimal output in fmtlib. The duplication exists for compatibility with `printf` conventions—`d` is the standard decimal specifier, while `i` matches `scanf`'s interpretation. In practice, always use `d` for clarity: `fmt::print("{:d}", -42)` produces `-42`.

### How do I left-pad a number with zeros in fmtlib?

Use the `0` flag or explicit fill-align syntax. The specifier `{:08}` produces `00000123` for input `123`. This is equivalent to `{:0>8}`—zero fill, right alignment, width 8. For numbers with signs, use `{:0=8}` to pad between sign and digits, giving `-0000123` for `-123`.

### Can I use fmtlib specifiers with printf-style functions?

Yes, through [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h). The `fmt::printf()` and `fmt::sprintf()` functions accept standard `printf` format strings, but internally map to fmtlib's type-safe engine. Specifiers like `%d`, `%x`, `%f` work directly, though you lose compile-time type checking compared to `fmt::format()`.

### Why does my custom type need the `?` specifier?

The `?` specifier invokes debug formatting via your `fmt::formatter<T>` specialization. It typically outputs a representation useful for debugging—often with type information or escaped strings. For standard types, `?` is not required; for custom types without explicit formatter support, it triggers the custom formatter if available.