<iomanip>
Parameterized stream manipulators for field width, fill character, numeric base, floating-point precision, monetary formatting, and date/time I/O.
<iomanip>since C++98The <iomanip> header provides parameterized stream manipulators β callable objects that modify a stream's formatting state when inserted into or extracted from it, distinguished from the no-argument manipulators (std::left, std::fixed, std::hex, etc.) defined in <ios> and exposed by <iostream>.
Overview
Stream manipulators fall into two families. Non-parameterized manipulators live in <ios> (transitively included by <iostream>) and need no arguments: std::left, std::fixed, std::hex, std::boolalpha, and so on. Parameterized manipulators require <iomanip> and take one or more arguments that configure the effect.
All manipulators are sticky except std::setw. Sticky means the formatting change persists for every subsequent I/O operation on that stream until explicitly reset. std::setw is consumed by exactly one insertion or extraction and must be reissued before each field that needs non-default width. This asymmetry is the root of the most common <iomanip> bugs.
Manipulator inventory
| Manipulator | Since | Sticky | Description |
|---|---|---|---|
setw(n) | C++98 | No | Minimum field width |
setprecision(n) | C++98 | Yes | Floating-point significant digits or decimal places |
setfill(c) | C++98 | Yes | Pad character (default ' ') |
setbase(b) | C++98 | Yes | Numeric base: 8, 10, or 16 |
setiosflags(mask) | C++98 | Yes | OR-sets named format flags |
resetiosflags(mask) | C++98 | Yes | Clears named format flags |
put_money(mon [, intl]) | C++11 | β | Inserts monetary amount using stream locale |
get_money(mon [, intl]) | C++11 | β | Extracts monetary amount using stream locale |
put_time(tm, fmt) | C++11 | β | Inserts std::tm as formatted date/time |
get_time(tm, fmt) | C++11 | β | Parses std::tm from formatted date/time |
quoted(s [, del [, esc]]) | C++14 | β | Round-trips quoted strings through streams |
Syntax
#include <iomanip>Manipulators are used inline with << for output streams and >> for input streams:
std::cout << std::setw(10) << std::setprecision(4) << std::fixed << value;
std::cin >> std::get_time(&t, "%Y-%m-%d"); // C++11Examples
Aligned table output
setw controls column width; setfill fills empty space; setprecision combined with std::fixed (from <ios>) controls decimal places:
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
int main() {
const std::vector<std::pair<std::string, double>> rows = {
{"Widget", 1.50},
{"Gadget", 199.99},
{"Doohickey", 0.75},
};
std::cout << std::left << std::setw(14) << "Item"
<< std::right << std::setw(10) << "Price" << '\n'
<< std::setfill('-') << std::setw(24) << "" << '\n'
<< std::setfill(' '); // reset fill
std::cout << std::fixed << std::setprecision(2); // sticky
for (auto& [name, price] : rows) { // C++17
std::cout << std::left << std::setw(14) << name
<< std::right << std::setw(10) << price << '\n';
}
}
// Item Price
// ------------------------
// Widget 1.50
// Gadget 199.99
// Doohickey 0.75Hexadecimal memory dump
setbase (or std::hex from <ios>) plus setw/setfill produce compact binary-inspection output:
#include <iomanip>
#include <iostream>
#include <cstddef>
#include <span> // C++20
void hex_dump(std::span<const std::byte> data, std::ostream& out) { // C++20
constexpr std::size_t cols = 16;
const auto saved_flags = out.flags();
const auto saved_fill = out.fill();
out << std::hex << std::setfill('0') << std::uppercase;
for (std::size_t i = 0; i < data.size(); i += cols) {
out << std::setw(8) << i << " ";
for (std::size_t j = 0; j < cols; ++j) {
if (i + j < data.size())
out << std::setw(2)
<< static_cast<unsigned>(data[i + j]) << ' ';
else
out << " ";
}
out << '\n';
}
out.flags(saved_flags);
out.fill(saved_fill);
}Monetary output (C++11)
put_money delegates to the std::money_put facet of the stream's imbued locale. The amount must be expressed in the locale's minor currency unit (cents for USD):
#include <iomanip>
#include <iostream>
#include <locale>
int main() {
std::cout.imbue(std::locale("en_US.UTF-8"));
const long double amount = 12345.67L;
std::cout << std::showbase
<< std::put_money(amount * 100) // C++11 β cents
<< '\n'; // $12,345.67
std::cout << std::put_money(amount * 100, true) // C++11 β international
<< '\n'; // USD 12,345.67
}Date/time I/O (C++11)
put_time and get_time wrap std::strftime/std::strptime via the std::time_put/std::time_get locale facets:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <ctime>
int main() {
// Write
std::time_t now = std::time(nullptr);
std::tm* local = std::localtime(&now);
std::cout << std::put_time(local, "%Y-%m-%d %H:%M:%S") << '\n'; // C++11
// Read β always value-initialize; get_time may leave unset fields undefined
std::tm parsed{};
std::istringstream ss("2026-06-15 09:30:00");
ss >> std::get_time(&parsed, "%Y-%m-%d %H:%M:%S"); // C++11
if (ss.fail())
std::cerr << "Parse error\n";
}Quoted strings (C++14)
std::quoted inserts a delimiter (default ") and escapes any occurrences of the delimiter or escape character inside the string. On extraction it strips the delimiters and unescapes:
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
int main() {
const std::string path = R"(C:\Program Files\My "App")";
std::ostringstream out;
out << std::quoted(path); // C++14
std::cout << out.str() << '\n';
// "C:\\Program Files\\My \"App\""
std::string recovered;
std::istringstream in(out.str());
in >> std::quoted(recovered); // C++14
// recovered == path β exact round-trip
}std::quoted is particularly useful for serialising paths or tokens that may contain spaces, since unquoted operator>> stops at whitespace.
Best Practices
Capture and restore stream state in formatting helpers. Because format flags, fill character, and precision are mutable stream state, any utility function that sets them without restoring them will corrupt the caller's formatting:
#include <iomanip>
#include <iostream>
void print_hex32(std::ostream& out, unsigned v) {
const auto flags = out.flags();
const char fill = out.fill();
const auto prec = out.precision();
out << "0x" << std::hex << std::uppercase
<< std::setfill('0') << std::setw(8) << v;
out.flags(flags);
out.fill(fill);
out.precision(prec);
}Prefer std::format (C++20) for new code. <iomanip> manipulators mutate hidden stream state and compose poorly; interleaved calls from different call sites corrupt each other silently. std::format from <format> is stateless, positional, type-safe, and roughly 2β3Γ faster to format:
#include <format> // C++20
#include <iostream>
// No state mutation, intent is local and explicit
std::cout << std::format("{:<14}{:>10.2f}\n", name, price);Use setiosflags/resetiosflags to set multiple flags atomically when you need to combine flags that std::hex etc. would set one at a time.
Common Pitfalls
setw is not sticky β the most common <iomanip> mistake. Applying setw once and then outputting multiple values gives the first value the desired width and all remaining values the default width of zero:
std::cout << std::setw(8); // applies only to the very next insertion
std::cout << a << b; // b gets width 0put_money and get_money silently produce wrong results in the "C" locale. The "C" locale defines no currency symbol or digit grouping. Imbue an explicit named locale before use, and handle the possibility that the locale name is not available on the target platform.
get_time does not zero-initialize std::tm. Fields not mentioned in the format string are left at whatever value the struct contained before the call. Always value-initialize with std::tm t{}; before passing it to get_time.
setbase silently falls back to base 10 for unsupported values. Arguments other than 8, 10, or 16 cause all basefield bits to be cleared, which the stream interprets as decimal. There is no error or diagnostic.
Stale precision after std::fixed. std::fixed changes what setprecision means: without it, precision counts significant figures; with it, precision counts decimal places. Switching between fixed and defaultfloat without re-issuing setprecision produces confusing output because the stored precision value carries over.
RAII Format State Guard
Stream formatting flags are shared mutable state β a function that sets them without restoring them corrupts every subsequent output call on that stream. The standard library's std::ios_base::flags(), std::ios_base::precision(), and std::basic_ios::fill() getters and setters make it straightforward to save and restore the complete formatting state with a small RAII guard.
The guard below captures all three pieces of state on construction and restores them unconditionally in its destructor, making it safe even when the formatting code throws:
#include <iomanip>
#include <iostream>
#include <ios>
class FormatGuard {
public:
explicit FormatGuard(std::ios_base& stream)
: stream_(stream),
flags_(stream.flags()),
precision_(stream.precision()),
fill_(static_cast<std::basic_ios<char>&>(stream).fill())
{}
~FormatGuard() {
stream_.flags(flags_);
stream_.precision(precision_);
static_cast<std::basic_ios<char>&>(stream_).fill(fill_);
}
FormatGuard(const FormatGuard&) = delete;
FormatGuard& operator=(const FormatGuard&) = delete;
private:
std::ios_base& stream_;
std::ios_base::fmtflags flags_;
std::streamsize precision_;
char fill_;
};The problem it solves: format state leaks silently across call boundaries. Without a guard, a hex dump helper leaves the stream in hex mode for all callers that follow:
// Without FormatGuard β state leak
void bad_hex_dump(std::ostream& out, unsigned v) {
out << std::hex << std::setfill('0') << std::setw(8) << v << '\n';
// hex and setfill remain set after this returns!
}
// With FormatGuard β caller's state is always restored
void hex_dump(std::ostream& out, unsigned v) {
FormatGuard guard(out); // saves state
out << "0x" << std::hex << std::uppercase
<< std::setfill('0') << std::setw(8) << v << '\n';
} // restores state
int main() {
hex_dump(std::cout, 0xDEADBEEF);
// Still in decimal here β FormatGuard ensured restoration
std::cout << 255 << '\n'; // prints 255, not ff
}Printing Tables and Aligned Output
std::setw controls the minimum field width of the very next insertion, while std::left and std::right choose which side to pad. Together with std::setfill for custom padding characters and std::setprecision for numeric columns, they are sufficient to produce readable fixed-width tables β provided you remember to reissue setw before every column (it is not sticky).
The following example prints a vector of result records as a formatted table with a separator line:
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
struct Result {
std::string name;
double score;
int rank;
};
void print_table(const std::vector<Result>& results) {
// Column widths
constexpr int W_NAME = 20;
constexpr int W_SCORE = 10;
constexpr int W_RANK = 6;
constexpr int W_TOTAL = W_NAME + W_SCORE + W_RANK;
std::cout << std::fixed << std::setprecision(2);
// Header
std::cout << std::left << std::setw(W_NAME) << "Name"
<< std::right << std::setw(W_SCORE) << "Score"
<< std::setw(W_RANK) << "Rank"
<< '\n';
// Separator
std::cout << std::setfill('-') << std::setw(W_TOTAL) << "" << '\n';
std::cout << std::setfill(' '); // reset fill β setfill is sticky
// Rows
for (const auto& r : results) { // C++11
std::cout << std::left << std::setw(W_NAME) << r.name
<< std::right << std::setw(W_SCORE) << r.score
<< std::setw(W_RANK) << r.rank
<< '\n';
}
}
int main() {
print_table({
{"Alice", 98.75, 1},
{"Bob", 87.50, 2},
{"Charlie",76.00, 3},
});
}
// Name Score Rank
// ------------------------------------
// Alice 98.75 1
// Bob 87.50 2
// Charlie 76.00 3setw only applies to the next output operation. Every column in every row needs its own setw call; a common mistake is to set it once at the top of a loop and expect it to apply across multiple << operators in the same statement.
Floating-Point Formatting: fixed, scientific, hexfloat
std::fixed, std::scientific, and std::hexfloat select the representation; std::setprecision controls the digit count within that representation. These manipulators are sticky β switch representations explicitly when you need to change back, and consider whether the stored precision value still makes sense after switching.
#include <iomanip>
#include <iostream>
#include <limits>
int main() {
const double pi = 3.141592653589793;
// Financial output: fixed decimal places
std::cout << std::fixed << std::setprecision(2) << pi << '\n';
// 3.14
// Engineering notation: exponent form
std::cout << std::scientific << std::setprecision(4) << pi << '\n';
// 3.1416e+00
// Exact binary representation β no rounding error // C++11
std::cout << std::hexfloat << pi << '\n';
// 0x1.921fb54442d18p+1
// Reset to default (significant digits, shortest representation)
std::cout << std::defaultfloat << pi << '\n';
// 3.14159
// Round-trip precision: use digits10 significant digits
std::cout << std::setprecision(std::numeric_limits<double>::digits10)
<< std::defaultfloat << pi << '\n';
// 3.14159265358979 (15 digits for double)
}std::hexfloat (C++11) is the only notation that guarantees an exact binary round-trip for IEEE 754 doubles. For serialisation of floating-point values where exact reproduction matters β configuration files, test fixtures, neural-network weights β prefer hexfloat or std::format("{:a}", v) (C++20) over fixed/scientific.
Integer Base Formatting: hex, oct, dec, showbase
std::hex, std::oct, and std::dec are sticky flags from <ios> that change how every subsequent integer is printed, so always reset to std::dec when you are done. std::showbase adds the conventional prefix (0x for hex, 0 for octal) so that the base is self-describing in the output.
#include <iomanip>
#include <iostream>
int main() {
const unsigned value = 255;
std::cout << std::hex << std::showbase;
std::cout << value << '\n'; // 0xff
std::cout << std::uppercase;
std::cout << value << '\n'; // 0XFF
std::cout << std::oct;
std::cout << value << '\n'; // 0377
std::cout << std::noshowbase << std::nouppercase;
// Zero-padded hex: setfill + setw every time (setw is not sticky)
std::cout << std::hex
<< std::setfill('0') << std::setw(8) << value << '\n';
// 000000ff
// Reset all integer base formatting to decimal
std::cout << std::dec << std::setfill(' ');
std::cout << value << '\n'; // 255
}Because these flags are sticky they persist across function calls, across loop iterations, and even across translation units that share the same stream object. A function that switches to std::hex for debug output and returns without calling std::dec will cause every integer output in the rest of the program to be printed in hex β often far from where the flag was set, making the bug difficult to locate.
Locale-Aware Formatting
std::imbue installs a locale on a stream, and all subsequent locale-sensitive operations β number grouping, decimal point character, monetary symbol β then use that locale's facets. std::locale("") constructs the locale from the process environment (the LANG/LC_ALL family of variables), giving the user's native formatting.
#include <iomanip>
#include <iostream>
#include <locale>
#include <sstream>
int main() {
// Print a large number with grouping separators (e.g. 1,234,567 in en_US)
std::cout.imbue(std::locale("en_US.UTF-8"));
std::cout << std::fixed << std::setprecision(2);
std::cout << 1234567.89 << '\n'; // 1,234,567.89
// German locale: decimal comma, dot thousands separator
std::cout.imbue(std::locale("de_DE.UTF-8"));
std::cout << 1234567.89 << '\n'; // 1.234.567,89
// Inspect the facet directly
std::locale loc("en_US.UTF-8");
const auto& np = std::use_facet<std::numpunct<char>>(loc);
std::cout.imbue(std::locale::classic()); // restore "C" locale
std::cout << "decimal point : " << np.decimal_point() << '\n'; // .
std::cout << "thousands sep : " << np.thousands_sep() << '\n'; // ,
std::cout << "grouping : ";
for (unsigned char g : np.grouping())
std::cout << static_cast<int>(g) << ' '; // 3 β groups of 3
std::cout << '\n';
}C++20 std::format is locale-independent by default, which avoids surprises when code runs in environments with unexpected locale settings. To opt into locale-sensitive formatting, pass std::locale to std::vformat or use the L conversion specifier (e.g. "{:L}") with a locale-aware format call. For most new code, the explicit opt-in model of std::format is preferable to <iomanip>'s implicit ambient-locale model.
iomanip vs std::format (C++20)
std::format (C++20) should be the default choice for new formatting code; <iomanip> remains the right tool for stream-based APIs, existing operator<< overloads, and pre-C++20 codebases. The two approaches differ across several axes:
| Criterion | <iomanip> | std::format (C++20) |
|---|---|---|
| Compile-time format safety | No β runtime failures or silent wrong output | Yes β ill-formed format strings are compiler errors (with std::format_string) |
| Performance | Moderate β virtual dispatch through std::locale facets | Fast β typically 2β4Γ faster than printf-family in benchmarks |
| Positional arguments | No | Yes β {0}, {1} for reordering |
| Locale handling | Implicit ambient locale; easy to corrupt globally | Locale-independent by default; opt-in with L specifier |
| Streaming API | Native β works with any std::ostream | Not streaming; produces std::string (use std::print for stdout) |
| Custom types | operator<< overload | std::formatter<T> specialisation |
operator<< integration | Direct | Requires adapter (e.g. out << std::format(...)) |
Converting an <iomanip>-based logger to std::format removes all hidden state and makes format strings auditable at a glance:
// Before β iomanip (C++98/11)
void log_event_old(std::ostream& out, const std::string& tag, double elapsed) {
const auto flags = out.flags();
out << "[" << std::left << std::setw(12) << tag << "] "
<< std::fixed << std::setprecision(3) << elapsed << "s\n";
out.flags(flags);
}
// After β std::format (C++20)
void log_event(std::ostream& out, std::string_view tag, double elapsed) {
out << std::format("[{:<12}] {:.3f}s\n", tag, elapsed); // C++20
}The std::format version has no mutable state, no save/restore boilerplate, and the complete format spec is visible inline. Use std::print instead of std::cout << when writing directly to stdout or stderr β it avoids constructing an intermediate std::string.
See Also
<ios>β non-parameterized format manipulators:std::fixed,std::hex,std::left,std::boolalpha, etc.<format>(C++20) βstd::formatandstd::print: stateless, type-safe replacement for most<iomanip>use cases<locale>βstd::money_put,std::money_get,std::time_put,std::time_getfacets underlying the C++11 manipulators<chrono>β C++20 adds directoperator<<forstd::chronoduration and time-point types, reducing the need forput_time