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
}
true false
1 0

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
}
-1 +1
-1 1

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
}
0XDEADBEEF 1.23457E+08
0xdeadbeef 1.23457e+08

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
}
1.00000 12.3400
1 12.34

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 << "], ";
	}
}
[a], [b], [c], [d],
[a], [ ], [b], [ ], [c], [ ], [d],

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 for std::cerr and std::wcerr); no effect on input

  • std::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";
}
1*******
10******
100*****
1000****
10000***
100000**
1000000*
10000000
100000000

.......1
......10
.....100
....1000
...10000
..100000
.1000000
10000000
100000000

0x_____1
0x_____a
0x____64
0x___3e8
0x__2710
0x_186a0
0x_f4240
0x989680
0x5f5e100

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 accuracy

  • double 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
+0 +0
+0 -0

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".