03 - formatted I/O
All streams in C++ can do formatted or unformatted I/O.
Unformatted I/O always behaves in the same way.
Formatted I/O means that resulting data is dependent on stream's state (which also includes currently imbued locale).
operators
<<
and>>
are intended for formatted I/O.Unformatted I/O and more formatted I/O is available through stream's member functions.
Locale is a pretty complex feature intended for localization which can affect formatted I/O and other specific text operations. Due to the size and uniqueness of the feature, it's outside the scope of the tutorial and instead is explained in a separate article TOWRITE. Custom locales are rarely used and the default "C" locale is universal to the point of being able to completely ignore this topic for now.
Everything below is more of a reference than actual lesson - don't try to memorize it. You will do so naturally when there will be a frequent need for such operations. Otherwise just come back here when an occasional need appears.
Formatting states
Each setting is represented by a specific bit in stream's flags. They can be changed using member functions and manipulators.
1 2 3 |
// https://en.cppreference.com/w/cpp/io/ios_base/setf stream.setf(std::ios::boolalpha); stream << std::boolalpha; |
Because some settings (e.g. base of the output - 8/10/16) are represented by separate bits, you need to set new one and unset previous one:
1 2 |
stream.unsetf(std::ios::dec); stream.setf(std::ios::hex); |
A safer shortcut for this operation is to use a different overload of setf
, one that takes an additional argument which specifies a group of bits to be cleared (called mask):
1 2 3 |
// set hex bit on, disable any other basefield bit // list of bits and masks: https://en.cppreference.com/w/cpp/io/ios_base/flags stream.setf(std::ios::hex, std::ios::basefield); |
Manipulators (which are passed with <<
) will automatically disable other bits when necessary.
Manipulators
Stream manipulators are functions which are either called (to create an object of specific but unspecified type) or passed directly (without calling) to stream insertion/extraction operators (which will call them internally). In any case, operator overloads are defined to support the way in which specific manipulator is intended to be used.
bool
Enable or disable printing words for boolean values (default: numeric).
1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << std::boolalpha << true << " " << false << "\n"; std::cout << std::noboolalpha << true << " " << false << "\n"; // default } |
These manipulators also affect input - after >> std::boolalpha
the stream will expect string representation of boolean values.
Positive numbers
Enable or disable printing +
for positive numbers (negative numbers are always printed with -
). Affects both integer and floating-point types.
1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << std::showpos << -1 << " " << 1 << "\n"; std::cout << std::noshowpos << -1 << " " << 1 << "\n"; // default } |
These manipulators have no effect on input.
Numeric system and prefix
Enable or disable printing base prefix (default: no prefix).
Specify which base should be used (default: decimal).
These manipulators apply only to integer types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int main() { std::cout << std::showbase; // enable printing base prefix (0 for octal, 0x for hex) std::cout << "in octal (base 8): " << std::oct << 42 << "\n"; std::cout << "in decimal (base 10): " << std::dec << 42 << "\n"; std::cout << "in hexadecimal (base 16): " << std::hex << 42 << "\n"; std::cout << "\n"; std::cout << std::noshowbase; // disable printing base prefix (default) std::cout << "in octal (base 8): " << std::oct << 42 << "\n"; std::cout << "in decimal (base 10): " << std::dec << 42 << "\n"; std::cout << "in hexadecimal (base 16): " << std::hex << 42 << "\n"; } |
in octal (base 8): 052 in decimal (base 10): 42 in hexadecimal (base 16): 0x2a in octal (base 8): 52 in decimal (base 10): 42 in hexadecimal (base 16): 2a
Strangely, there is no std::bin
that would print numbers in binary. As a workaround, << std::bitset<N>(val)
can be used.
Prefix manipulators also affect input but only monetary input which I guess no one uses.
Base manipulators affect both ouput and input. Because each of these 3 manipulators is stored on a separate bit in the stream state flags, there is a possibility that no bit will be set. In such case:
output is decimal
input is prefix dependent (no prefix means decimal)
All base state bits can be cleared in 2 ways:
>> std::setbase(0)
or<< std::setbase(0)
stream.setf(std::ios_base::fmtflags(0), std::ios_base::basefield)
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> #include <sstream> int main() { int x = 0; int y = 0; std::istringstream iss("10 010"); // comment/uncomment line below for different parsing behavior // iss.setf(std::ios_base::fmtflags(0), std::ios_base::basefield); iss >> x >> y; // commented: 10 10 (default state has dec bit on), // uncommented: 10 8 (no base bits cause parsing to depend on prefix) std::cout << x << " " << y << "\n"; } |
What if multiple bits are set?
No idea, nothing about in on cppreference so I guess the behavior is unspecified.
Casing
Use uppercase or lowercase letters for numerical output (default: lowercase).
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { std::cout << std::hex << std::showbase; std::cout << std::uppercase << 0xdeadbeef << " " << 123456789.0 << "\n"; std::cout << std::nouppercase << 0xdeadbeef << " " << 123456789.0 << "\n"; // default } |
You can also observe that default format for floating-point types rounds numbers when using scientific notation.
These manipulators are only for numeric types and do not affect printing text in any way. They have no effect on input.
Point
Enable or disable printing point and decimal fraction digits when not necessary (default: off).
1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << std::showpoint << 1.0 << " " << 12.34 << "\n"; std::cout << std::noshowpoint << 1.0 << " " << 12.34 << "\n"; // default } |
This setting is locale-dependent - locales may specify different character than .
.
These manipulators have no effect on input.
Floating-point formats
Specify which format should be used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int main() { std::cout << "0.001 in fixed: " << std::fixed << 0.001 << "\n"; // ignores showpoint std::cout << "0.001 in scientific: " << std::scientific << 0.001 << "\n"; std::cout << "0.001 in hexfloat: " << std::hexfloat << 0.001 << "\n"; // ignores precision std::cout << "0.001 in default: " << std::defaultfloat << 0.001 << "\n"; // this is the default std::cout << "\n"; std::cout << " 1000 in fixed: " << std::fixed << 1000.0 << "\n"; std::cout << " 1000 in scientific: " << std::scientific << 1000.0 << "\n"; std::cout << " 1000 in hexfloat: " << std::hexfloat << 1000.0 << "\n"; std::cout << " 1000 in default: " << std::defaultfloat << 1000.0 << "\n"; } |
0.001 in fixed: 0.001000 0.001 in scientific: 1.000000e-03 0.001 in hexfloat: 0x1.0624dd2f1a9fcp-10 0.001 in default: 0.001 1000 in fixed: 1000.000000 1000 in scientific: 1.000000e+03 1000 in hexfloat: 0x1.f4p+9 1000 in default: 1000
These manipulators have no effect on input: parsing of floating-point numbers is unaffected.
Whitespace
Enable or disable skipping whitespace before formatted input operations. By default, whitespace is skipped.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <sstream> int main() { const auto text = "a b c d"; std::istringstream iss; char c = '\0'; iss.str(text); iss >> std::skipws; // default behavior (this statement has no effect) while (iss >> c) // will stop on reaching end of internal string buffer and set EOF { std::cout << "[" << c << "], "; } std::cout << "\n"; iss.clear(); // clear EOF iss.str(text); iss >> std::noskipws; while (iss >> c) { std::cout << "[" << c << "], "; } } |
These manipulators have no effect on output.
Whitespace can also be skipped explicitly by doing >> std::ws
. This operation will consume any consecutive whitespace in the input stream.
Other
There are 2 more manipulators:
std::unitbuf
,std::nounitbuf
- disable or enable output buffering (buffering is disabled forstd::cerr
andstd::wcerr
); no effect on inputstd::left
,std::right
,std::internal
- showcased further down as these manipulators have effect only in combination with width and fill manipulators
Additional manipulators
These manipulators are defined in <iomanip>
.
Adjustment
Width:
Unlike other manipulators,
std::setw
(set width) is temporary: it only affects next formatted I/O operation. Thus, it's typically used inside loops.For formatted output operations, it specifies the minimum amount of characters that should be printed. Additional characters are repetitions of the fill character.
For formatted input operations, see next example.
By default width is zero, which means no additional characters are printed.
Fill:
Specifies the fill character. Default is space.
Adjustment:
Specify positioning of fill characters.
Default adjustment is right, but any adjustment has effect only if width forces to print fill characters. Internal adjustment will separate all kinds of prefixes (
0x
for hexadecimal,-
for negative numbers and currency symbols if monetary output is used).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <iomanip> int main() { // adjust left and fill with * std::cout << std::left << std::setfill('*'); for (long i = 1; i < 1000000000; i *= 10) std::cout << std::setw(8) << i << "\n"; std::cout << "\n"; // adjust right and fill with . std::cout << std::right << std::setfill('.'); for (long i = 1; i < 1000000000; i *= 10) std::cout << std::setw(8) << i << "\n"; std::cout << "\n"; // use hex base and print prefix, fill with _ // adjust numbers to the right but other characters to the left std::cout << std::hex << std::showbase; std::cout << std::internal << std::setfill('_'); for (long i = 1; i < 1000000000; i *= 10) std::cout << std::setw(8) << i << "\n"; } |
Precision
Specify minimum amount of digits that should be printed. This affects only floating-point output.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> #include <iomanip> #include <cmath> #include <limits> int main() { const long double pi = std::acos(-1.0l); // (arc cosine) constexpr auto maxp = std::numeric_limits<long double>::digits10 + 1; std::cout << "default precision (6): " << pi << "\n" << "std::setprecision(10): " << std::setprecision(10) << pi << "\n" << "max precision : " << std::setprecision(maxp) << pi << "\n\n"; // This value will likely not be ideal half due to // limited accuracy of floating-point computations. const auto almost_half = std::acos(0.0) / pi; // Setting precision larger than supported by the underlying // floating point type will cause weird output. // This is because streams always try to print the value (in decimal) // that is closest to the actually stored value (in floating-point). // Thus, 0.5 is choosen over 0.4999999999999999 (the first one is more accurate). // Once the precision allows to represent this tiny error, output changes. for (int i = 6; i < maxp + 3; ++i) std::cout << std::setprecision(i) << almost_half << "\n"; } |
As a rough guideline:
float
has 6 digits of accuracydouble
has 15-
long double
is implementation-defined:18 in case of Intel's 80-bit floating-point type
33 in case of true IEEE quadruple floating-point type
Other
https://en.cppreference.com/w/cpp/header/iomanip lists a few more but they are very specific and have lots of tiny details. They are hardly ever used.
Exercise
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { std::cout << std::showpos; std::cout << 0 << " " << -0 << "\n"; std::cout << 0.0 << " " << -0.0 << "\n"; } |
What does the following program print?
answer
And why?
answer
Integers have only one representation of 0 and it's regarded as positive (in true mathematical sense 0 is neither positive nor negative). Negating integer 0 does nothing to the underlying bit pattern. Unlike integer types (which use two's complement), floating-point types have a separate bit for sign and thus allow multiple representations for 0, thus they have both "positive zero" and "negative zero".