# How to Format Hexadecimal Numbers with Signs and Zero-Padding in fmtlib

> Learn to format hexadecimal numbers with signs and zero-padding in fmtlib using the {:+#010x} format string. Control sign, prefix, padding, and width for perfect output.

- Repository: [Hello World Foundation/fmt](https://github.com/fmtlib/fmt)
- Tags: how-to-guide
- Published: 2026-09-08

---

**Use the format string `"{:+#010x}"` where `+` forces a sign, `#` adds the `0x` prefix, `0` enables zero-padding, and `10` sets the total field width.**

Formatting integers as hexadecimal with explicit positive signs and zero-padding is essential for low-level debugging, memory dumps, and network protocol analysis. The **fmtlib/fmt** library provides precise control over these output characteristics through its type-safe format specification API. Understanding the internal mechanics—from the `format_specs` parser to the `write_int` implementation—ensures you can predict exactly how signs, prefixes, and padding interact.

## Anatomy of the Format Specification

When you write `fmt::format("{:+#010x}", 12345)`, the library parses the string between the braces into a `format_specs` object. This structure captures four critical properties:

- **Sign**: Controlled by `+` (always show sign) or space (leading space for positive), stored in `format_specs::sign` as defined in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h).
- **Fill/Zero-Padding**: The `0` flag sets `format_specs::fill` to `'0'`, triggering zero-fill logic later in the pipeline.
- **Alternate Form**: The `#` flag indicates that hexadecimal numbers should include the `0x` or `0X` prefix.
- **Width and Type**: The integer `10` sets the total field width, while `x` specifies lowercase hexadecimal presentation.

## Implementation in the fmtlib Source Code

The conversion from format string to output characters traverses several internal functions in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h).

### Detecting the Sign and Zero Flags

Legacy format parsing in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) (line 318) maps character flags to internal specifiers:

```cpp
case '+': specs.set_sign(sign::plus); break;

```

For zero-padding, the library detects when the fill character is `'0'` at lines 2581–2583 of [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h):

```cpp
const bool is_zero_fill = specs.fill_size() == 1 && specs.fill_unit<Char>() == '0';

```

When `is_zero_fill` is true, the padding logic knows to insert zeros between the sign/prefix and the digits rather than using the default space-based alignment.

### Constructing the Sign and Prefix

The function `make_write_int_arg` (lines 42–46 in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)) creates a packed representation of the prefix that includes both the sign and the hexadecimal base indicator. For positive values with the `+` flag, it constructs:

```cpp
constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '};
prefix = prefixes[static_cast<int>(s)];

```

If the alternate form (`#`) is requested for hexadecimal output, `prefix_append` (lines 3113–3115) merges the `0x` or `0X` characters into the existing prefix:

```cpp
prefix_append(prefix, unsigned(specs.upper() ? 'X' : 'x') << 8 | '0');

```

This ensures the sign character appears first, followed immediately by the base prefix.

### Writing the Padded Output

Finally, `write_int` (lines 3150–3152 in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h)) emits the formatted number. After writing the prefix (sign + `0x`), it calculates the remaining padding required to reach the specified width and fills with zeros:

```cpp
it = detail::fill_n(it, padding, static_cast<Char>('0'));

```

This step produces the final string where zeros appear between the prefix and the hexadecimal digits, ensuring the total output length matches the requested width.

## Practical Examples

The following program demonstrates combining signs, zero-padding, and hexadecimal formatting:

```cpp
#include <fmt/core.h>
#include <cstdint>
#include <iostream>

int main() {
    std::int32_t pos = 12345;   // 0x3039
    std::int32_t neg = -12345;  // -0x3039

    // Sign, zero-padding, width 8, lower-case hex, no prefix
    std::cout << fmt::format("{:+08x}\n", pos);   // +0003039
    std::cout << fmt::format("{:+08x}\n", neg);   // -0003039

    // Sign, zero-padding, width 10, alternate form (adds 0x)
    std::cout << fmt::format("{:+#010x}\n", pos); // +0x0003039
    std::cout << fmt::format("{:+#010x}\n", neg); // -0x0003039

    // Space flag instead of plus, upper-case hex, width 12
    std::cout << fmt::format("{: #012X}\n", pos); //  0X0003039
    std::cout << fmt::format("{: #012X}\n", neg); // -0X0003039
}

```

**Key observations from the examples:**

- `"{:+08x}"` produces `+0003039` because the width `8` includes the sign character but excludes the hexadecimal prefix.
- `"{:+#010x}"` produces `+0x0003039` (10 characters total) because the width includes the sign, the `0x` prefix, and the digits.
- `"{: #012X}"` uses a space for positive signs and upper-case letters, yielding ` 0X0003039` (note the leading space before `0X`).

## Summary

- **fmtlib** stores format parameters in a `format_specs` object that captures sign, fill, width, and presentation type.
- The `+` flag forces explicit signs for positive numbers, parsed in [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) and stored via `enum class sign` in [`include/fmt/core.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/core.h).
- Zero-padding occurs when `is_zero_fill` detects a `'0'` fill character in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), ensuring zeros appear between the prefix and digits.
- The `write_int` function in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) finalizes output by combining the sign, optional `0x` prefix (added by `prefix_append`), and zero-filled digits to meet width requirements.

## Frequently Asked Questions

### Does the field width count the sign and the 0x prefix?

Yes. In fmtlib, the width parameter specifies the total number of characters in the final output, including the sign character and the `0x` or `0X` prefix added by the `#` flag. For example, `"{:+#010x}"` generates exactly 10 characters.

### Can I use a space instead of a plus sign for positive numbers?

Yes. Replace the `+` with a space in the format string (e.g., `"{: #08x}"`). The library stores this as `sign::space` and inserts a leading space for positive values while negative values retain the minus sign.

### What happens if I remove the zero flag but keep the width?

Without the `0` flag, fmtlib uses the default fill character (space) and aligns the output to the right. The padding spaces appear before the sign and prefix, resulting in output like `"   +0x3039"` instead of `"+0x0003039"`.

### Which header should I include for hexadecimal formatting?

Include `<fmt/core.h>` for the main `fmt::format` function. The internal implementations in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and [`include/fmt/printf.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/printf.h) are pulled in automatically by the core header; you do not need to include them directly.