Boost::format (and wformat) examples – numbers: int, float, double

By , last updated May 22, 2017

Boost Format is a very versatile and easy to use formatting library. If you’re using Boost, then there is no point in using other formatting libraries for formatting. For most uses, boost::format and boost::wformat are perfectly suitable.

Boost Format is not as fast a sprintf and that family of formatters, but it is type safe and robust. It uses streams to construct the output, so there is no chance of buffer overflow and other nasal demons coming out of your nose. According to that table, Boost Spirit Karma is even faster. That’s worthy a blog post in itself, but this is about boost::format.

This is a continuation of the first boost format post, with more images and descriptions.

Basic number formatting

I’ve used boost::format in many projects and it has an easy syntax once you get to know it. It has two types of formatting rules, one is printf-like formatting and the other is positional arguments (like formatting in C#). I’ve never had a reason to use positional notation, however they are very similar to the print-like formatting rules and the advantage is that you can switch the order of arguments during runtime (think i18n and language translation support).

Example with default floating point precision formatting

Formatting floating point numbers is one of the most used features of string formatting. There aren’t many other ways of outputting floating point numbers than use formatting of some sort.

const double almostpi = 22.0 / 7.0;

// Printf
printf("Pi is %fn", almostpi);

// Boost format with printf syntax
boost::format printf_formatting("Pi is %fn");
std::cout << printf_formatting % almostpi;

// Boost format with positional syntax
boost::format position_formatting("Pi is %1%n");
std::cout << position_formatting % almostpi;

// Output:
// Pi is 3.142857
// Pi is 3.142857
// Pi is 3.14286

The archaic method printf is just here as a comparison. It should not be used in new code as this family of formatters is regarded unsafe.

Lets see what is happening here. The formatting rule %f tells the printf method to format a floating point number using default rules, which is about 6 decimal points.

Floating point replacement of %f

At %f the contents of the variable almostpi will be expanded and output into the standard output, which normally is the console window. The program will output: Pi is 3.142857

There is really nothing more magic than that. The rest of the options are just variations of the same. Replace a specific code with some contents.

The same diagram for boost::format with printf-style formatting rules.

Boost::format %f

Default formatting with a long floating point number

Formatting large numbers are similar to those small numbers.

const double almostpi = 22.0 / 7.0;
const double distancetosun = 149600000000.0;    // meters
const double earthorbitlength = 2 * almostpi * distancetosun;

// Printf
printf("Earth orbit distance is %f metersn", earthorbitlength);

// Boost format with printf syntax
boost::format printf_formatting("Earth orbit distance is %f metersn");
std::cout << printf_formatting % earthorbitlength;

// Boost format with positional syntax
boost::format position_formatting("Earth orbit distance is %1% metersn");
std::cout << position_formatting % earthorbitlength;

// Output:
// Earth orbit distance is 940342857142.857180 meters
// Earth orbit distance is 940342857142.857180 meters
// Earth orbit distance is 9.40343e+011 meters

The default precision is still 6 decimal points, however the %1% formatter decided scientific notation was a better fit. More on that later.

Controlling precision with floating point number format

The format specifier for old style print is “%[flags][width][.precision][length]specifier “.

The format specifier for boost format positional notation is: “%[N$][flags][width][.precision]type-char“.

Both formats are very similar, and there are only a few actual differences between those two specifiers.

For most purposes, the default settings are sufficient when you force the type of the format, except when using the default %1% type. It’ll switch over to scientific notation around 6-7 characters length.

When formatting large numbers, the number will never be truncated. That decision is a trade-off between wanting to show the correct number or use the correct formatting. It’s easier to see the formatting is off, than to figure out the number is missing some integers in one or other end in some printout.

// Printf
printf("Pi is '%10.2f'n", almostpi);

// Boost format with printf syntax
boost::format printf_formatting("Pi is '%10.2f'n");
std::cout << printf_formatting % almostpi;

// Boost format with positional syntax
boost::format position_formatting("Pi is '%1$10.2f'n");
std::cout << position_formatting % almostpi;

// Output:
// Pi is '      3.14'
// Pi is '      3.14'
// Pi is '      3.14'

Format with precision

Numbers are not truncated when they overflow the width.

// Printf
printf("Earth orbit distance is '%5.2f' metersn", earthorbitlength);

// Boost format with printf syntax
boost::format printf_formatting("Earth orbit distance is '%5.2f' metersn");
std::cout << printf_formatting % earthorbitlength;

// Boost format with positional notation
boost::format position_formatting("Earth orbit distance is '%1$5.2f' metersn");
std::cout << position_formatting % earthorbitlength;

// Output:
// Earth orbit distance is '940342857142.86' meters
// Earth orbit distance is '940342857142.86' meters
// Earth orbit distance is '940342857142.86' meters

When scientific notation becomes the default

At some point, the positional notation formatter will switch to scientific notation when you’re not using any type specifiers. Here is a small test demonstrating when it will switch from floating point representation and over to scientific notatation.

boost::format scinotation("Do we use scientific? '%1%'n");

for (size_t n=0; n<15; ++n)
{
    double value = 1 * pow(10, n) + 0.5;
    std::cout << scinotation % value;
}

// Output:
// Do we use scientific? '1.5'
// Do we use scientific? '10.5'
// Do we use scientific? '100.5'
// Do we use scientific? '1000.5'
// Do we use scientific? '10000.5'
// Do we use scientific? '100001'
// Do we use scientific? '1e+006'
// Do we use scientific? '1e+007'
// Do we use scientific? '1e+008'
// Do we use scientific? '1e+009'
// Do we use scientific? '1e+010'
// Do we use scientific? '1e+011'
// Do we use scientific? '1e+012'
// Do we use scientific? '1e+013'
// Do we use scientific? '1e+014'

Using boost::wformat for i18n (Internationalization)

Here is a brief example on how to use the positional notation for translations with wformat.

// Language string input parameters
// 1 - Title
// 2 - Given name
// 3 - Family name

std::wstring english = L"Hello, %1% %3%, %2%n";
std::wstring norwegian = L"Hei %2% %3%n";
std::wstring russian = L"здравствуйте %2% %3%";

std::wstring title = L"Mr.";
std::wstring name = L"John";
std::wstring family = L"Doe";

std::wcout << "English: " << boost::wformat(english) % title % name % family;
std::wcout << "Norwegian: " << boost::wformat(norwegian) % title % name % family;
std::wcout << "Russian: " << boost::wformat(russian) % title % name % family;

// Output:
// English: Hello, Mr.Doe, John
// Norwegian: Hei John Doe
// Russian: здравствуйте John Doe

The idea is that for each string requiring formatting, there must be a predefined set of keys. In this example, all three (title, name, family name) is given into the boost::wformat object, but only the necessary parameters are used. This is (almost) impossible with the printf-family.

With positional notation, it’s fully supported to use a different order than the order of parameters.

Read also: Boost format examples: dates, time, alignment, loops.
String format

Why is the printf-family of formatters considered unsafe?

This is a very basic example on how easy it is to corrupt data on the stack with printf / sprintf. Due to how different compilers and runtimes do things, the result is undefined. It might format your computer, but most times this line will corrupt the stack space and hopefully crash with a meaningful message. Visual Studio will usually detect stack space corruption during debug, since it uses canaries around variables which is set to a certain value (also known as no-mans-land). If the value is not as expected, an assert will trigger and notify the debugger there are problems.

char important[10] = {0};	// Initialize to nulls
char characters[10] = {0};	// Initialize to nulls
char important2[10] = {0};	// Initialize to nulls

sprintf(characters, "Overflow '%5.2f'", almostpi);

// At this point, the stack space should be corrupted around 'characters'

With Visual Studio, you’ll usually get this warning if you’re in debug mode.

Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.

Comments

  1. Baggins March 8, 2015 Leave a Reply

    Great follow-up article, thanks! I keep coming back to the old one whenever I need to remind myself how boost::format works.

    • kent March 13, 2015 Leave a Reply

      Thanks, boost::format is very useful and versatile. Soon, I’ll publish a post with more advanced uses of boost::format, including variadic templates as a drop-in replacement for printf-style loggers.

Leave a Reply


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*