# How fmtlib Handles NaN and Infinity in C++ Floating-Point Formatting

> Learn how fmtlib formats NaN and infinity in C++ by routing them to write_nonfinite for literal string output, detecting them with isnan and range checks.

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

---

**fmtlib routes special floating-point values through `write_nonfinite` to output literal `"nan"` or `"inf"` strings, detecting them via `detail::isnan` and infinity range checks while respecting case specifiers and alignment rules.**

The {fmt} library provides type-safe formatting for C++ applications, including standardized handling of IEEE-754 special values. When formatting NaN (Not a Number) or infinite floating-point values, fmtlib bypasses normal decimal conversion algorithms and writes canonical string representations. This implementation resides primarily in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) and ensures consistent output across `fmt::format`, `fmt::print`, and related APIs.

## Detecting Special Floating-Point Values in fmtlib

Before formatting occurs, fmtlib distinguishes between finite numbers and special values using custom detection logic. This ensures portability across different floating-point types and compilers.

### NaN Detection via detail::isnan

The library uses a custom `detail::isnan` helper defined in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h) rather than relying solely on `std::isnan`. This wrapper ensures compatibility across all floating-point types, including extended precision types like `__float128` where standard library support may be unavailable.

During formatting, the main path at line 3682 of [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h) invokes this check. If `detail::isnan(value)` returns true, the pipeline immediately flags the value as non-finite and skips the Dragonbox decimal conversion algorithm.

### Infinity Checking in isfinite

For infinity detection, fmtlib compares the value against a compile-time constant `inf` derived from `std::numeric_limits<T>::infinity()`. The `isfinite` helper, located at lines 2857-2864 in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), evaluates whether the value lies within the finite range.

If the value is not NaN and fails the bounds check `value < inf && value > -inf`, fmtlib classifies it as infinite. This identifies both positive and negative infinity before the presentation type is selected.

## The write_nonfinite Formatting Pipeline

Once a value is identified as non-finite, control flows to `fmt::write_nonfinite` at lines 2574-2579 of [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h). This function receives three critical parameters:

- `isnan`: A boolean distinguishing NaN from infinity
- `sign`: A flag indicating negative infinity (NaN has no sign)
- Format specifications including the `upper` flag for case conversion

The function selects the appropriate literal string—`"nan"` for NaN or `"inf"` for infinity—and applies uppercase transformation when the format string contains the `L` flag (e.g., `{:L}`).

### Padding and Alignment Rules

Non-finite values support width and alignment specifiers, but with a specific restriction. According to the `is_zero_fill` check at lines 8181-8184, zero-padding is replaced by space-padding for non-finite outputs. This prevents ambiguous representations like `000nan` while preserving the requested field width and alignment.

## Formatting Examples with Special Values

The following example demonstrates default lowercase output, uppercase conversion, and width alignment:

```cpp
#include <fmt/core.h>
#include <cmath>
#include <limits>

int main() {
    double nan_val = std::nan("1");
    double pos_inf = std::numeric_limits<double>::infinity();
    double neg_inf = -std::numeric_limits<double>::infinity();

    // Default output: lowercase nan/inf
    fmt::print("Default: {} {} {}\n", nan_val, pos_inf, neg_inf);
    // Output: nan inf -inf

    // Uppercase specifier with 'L' flag
    fmt::print("Upper: {:L} {:L} {:L}\n", nan_val, pos_inf, neg_inf);
    // Output: NAN INF -INF

    // Width and alignment (10 characters wide, right-aligned)
    fmt::print("Aligned: '{:>10}' '{:>10}'\n", nan_val, pos_inf);
    // Output: '       nan' '       inf'
}

```

## Chrono Library Integration

The formatting logic extends to time calculations through [`include/fmt/chrono.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/chrono.h). When formatting chrono durations or time points containing NaN values, the library forwards to a specialized `write_nan` function that outputs `"nan"` directly. This maintains consistency with the core floating-point formatting pipeline across all fmtlib APIs.

## Summary

- **Detection**: fmtlib identifies NaN via `detail::isnan` and infinity via range checks against `std::numeric_limits<T>::infinity()` in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).
- **Output**: Special values route through `write_nonfinite` (lines 2574-2579) to produce literal `"nan"` or `"inf"` strings.
- **Case Control**: The `L` format specifier converts output to uppercase (`NAN`, `INF`).
- **Padding**: Zero-fill is disabled for non-finite values, automatically converting to space-padding per lines 8181-8184.
- **Consistency**: The same logic applies across `fmt::format`, `fmt::print`, and chrono formatting APIs.

## Frequently Asked Questions

### How does fmtlib format negative infinity?

When formatting negative infinity, fmtlib detects the sign during the `isfinite` check and passes it to `write_nonfinite`. The function prepends a minus sign to the `"inf"` literal, producing `"-inf"` by default or `"-INF"` when using the uppercase `L` specifier.

### Can I customize the string output for NaN values in fmtlib?

No, fmtlib uses hardcoded literals `"nan"` and `"NAN"` within the `write_nonfinite` function. Unlike finite floating-point formatting that supports precision and notation specifiers, non-finite values have fixed string representations defined in the source code at [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h).

### Does fmtlib handle NaN for extended precision types like __float128?

Yes, the custom `detail::isnan` implementation in [`include/fmt/format-inl.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format-inl.h) provides portability for floating-point types beyond standard `double` and `float`, including `__float128` where `std::isnan` might not be available.

### Why does zero-padding not work with NaN and infinity in fmtlib?

According to lines 8181-8184 in [`include/fmt/format.h`](https://github.com/fmtlib/fmt/blob/main/include/fmt/format.h), fmtlib explicitly replaces zero-fill with space-fill for non-finite values. This design choice prevents confusing numeric representations (like `0000nan`) while maintaining the specified field width and alignment.