Is the Dinosaur Still King?
I have used and abused the printf function since the 80’s. Yes, the NINETEEN 80’s. My first C compiler was Power C on a Commodore C64. By the time I picked up C, I had learned BASIC and Assembly Language (a mnemonic machine language). You can imagine the smile on my face when I read the brochure. Basic and Assembly were notoriously difficult to print decimal places, but C made it easy. Not only can I print the correct number of places after the decimal, but have it round for me and space it all out. I was SOLD.
Since then, I have found a version of my beloved printf function in the next generation of C, C++. Even C# has a String.Format function that looks a lot like the printf function.
C++ introduced a lot of new concepts and capabilities. It introduced a whole new paradigm of programming – the Class. Object Oriented Programming, OOP. But, it also introduced a new guy on the block, cout. That’s SEE-OUT.
cout is simple to use in C++:
cout << "Hello foo!" << endl;
whereas C and C++ could use printf:
printf("Hello foo!\n");
Lets Print Some Floating Point Numbers
printf Version:
float F1 = 0.36789;
printf("%7.2f\n", F1);
>> 0.37
cout Version:
float F1 = 0.36789;
std::cout << std::setfill(' ') << std::setw(7) << std::fixed << std::setprecision(2) << F1 << endl;
>> 0.37
The verdict? printf wins hands down. For those of you who rely on AutoComplete, you’ll appreciate the cout version since you can literally CTRL-SPACE your way to an end product. But, you can’t deny the printf version is a whole lot simpler and requires WAY less typing.
But… is printf FASTER than cout?
Comparing Speed
I wrote a quick program, shown below to test the speed of each method. Could it be that cout uses more resources? Is printf “closer” to the hardware? Let’s take a look at the numbers and see if your project could see a speed boost.
Printing 20,000 Integers
using namespace std;
#include
#include
#include
#include
int main()
{
int MaxIterations = 20000;
auto Start1 = std::chrono::high_resolution_clock::now();
for (int x = 0; x < MaxIterations; x++)
printf("%6d\r",x); // print XXXX
auto Finish1 = std::chrono::high_resolution_clock::now();
std::cout << endl << MaxIterations << " Iterations with PRINTF took " << std::chrono::duration_cast(Finish1 - Start1).count() << "mS\n";
auto Start2 = std::chrono::high_resolution_clock::now();
for (int x = 0; x < MaxIterations; x++)
std::cout << std::setfill(' ') << std::setw(6) << std::fixed << x << '\r';
auto Finish2 = std::chrono::high_resolution_clock::now();
std::cout << endl << MaxIterations << " Iterations with COUT took " << std::chrono::duration_cast(Finish2 - Start2).count() << "mS\n";
}
And the results...
19999
20000 Iterations with PRINTF took 881mS
19999
20000 Iterations with COUT took 4630mS
The printf function was able to print 20,000 integers in 881mS while the gimpy cout clocked in at 4630mS, a whopping 5.25 times slower.
Lets try something a little more challenging, such as printing a formatted floating point number.
Printing 20,000 Floats
using namespace std;
#include
#include
#include
#include
int main()
{
int MaxF = 10000;
auto Start1 = std::chrono::high_resolution_clock::now();
for (float f = 0; f < MaxF; f+=0.5)
{
printf("%5.2f\r", f);
}
auto Finish1 = std::chrono::high_resolution_clock::now();
std::cout << endl << MaxF * 2 << " Iterations with PRINTF took " << std::chrono::duration_cast(Finish1 - Start1).count() << "mS\n";
printf("\n");
auto Start2 = std::chrono::high_resolution_clock::now();
for (float f = 0; f < MaxF; f += 0.5)
{
std::cout << std::setfill(' ') << std::setw(5) << std::fixed << std::setprecision(2) << f << '\r';
}
auto Finish2 = std::chrono::high_resolution_clock::now();
std::cout << endl << MaxF*2 << " Iterations with COUT took " << std::chrono::duration_cast(Finish2 - Start2).count() << "mS\n";
}
9999.50
20000 Iterations with PRINTF took 1015mS
9999.50
20000 Iterations with COUT took 5298mS
While the printf function turned in a respectable 1015mS to print 20,000 floating-point numbers with padding and truncation, cout turned in a paltry 5298mS – a full 5 times slower.
Type Safety
Every once in a while a programmer will make a mistake. Its true. One such mistake is mismatching types when using printf. Here’s an example.
printf("%f is a float!\n", "Oops");
Instructing printf to work with a float, %f, then providing a string pointer will compile, with warnings, but could be unpredictable at best and crash at worst.
By its nature, cout is type safe, meaning, it won’t even compile without first ensuring it can handle the types of variables you’re throwing at it.
Conclusion
While cout has its uses, it’s clearly outclassed by its older ancestor, printf.
I’ve shown that printf can increase your output by 5 times under heavy load and is simpler to read and write.
Learning printf in today’s programming environment is a worthwhile endevour and will pay back in faster, more readable programs.
As it turns out, “new things” aren’t always replacements for “old things” and in this case, printf is still the king.