Equivalent of printf %g flag for stream output (e.g. for std::cout)?

151 Views Asked by At

If I need to print a floating point number x I normally use printf("%g", x) as this is the simplest and the most convenient, for me, way to output floating point number in human-readable format where possible and automatically switch to exponential form for too large or too small values.

With std::cout and similar output streams it is not that simple. Say, I have class point_2d which overloads operator<< without changing stream's settings. E.g.:

ostream& operator<<(ostream& os, const point_2d& p)
{
    os << '(' << p.x << "; " << p.y << ')';
    return os;
}

If I do

std::cout << point_2d_instance << std::endl;

the output will be in whatever format was set for std::cout by this point.

I cannot change operator<< implementation and I do not know the current state of std::cout. Which flags should I set in std::cout to get an equivalent of

printf("(%g; %g)", p.x, p.y);
2

There are 2 best solutions below

0
KamilCuk On BEST ANSWER

Equivalent of printf %g flag for stream output (e.g. for std::cout)?

std::defaultfloat or the default.

the output will be in whatever format was set for std::cout by this point.

If you depend on a particular format when working with streams operator, you should store the flags set on the stream, set your own flags, then restore original flags.

#include <iostream>
#include <ios>
using namespace std;
struct point_2d { double x; double y; };

ostream& operator<<(ostream& os, const point_2d& p) {
    std::ios::fmtflags oldflags(os.flags());
    os << std::defaultfloat << '(' << p.x << "; " << p.y << ')';
    os.flags(oldflags);
    return os;
}

int main() {
    std::cout << point_2d{0.1, 0.000000001} << '\n';
}
0
vitaut On

One option is to use C++20 std::format or the open-source {fmt} library which are not affected by the std::ostream state:

std::ostream& operator<<(std::ostream& os, const point_2d& p) {
  return os << std::format("({}; {})", p.x, p.y);
}

This has an additional advantage of avoiding precision loss by default and you can control precision via format specifiers. You can also use the g specifier which has the same semantics as in printf except that it is not affected by the locale by default.

Or avoid ostream altogether and define a formatter specialization for your type which will make it work with both std::format and std::ostream (via std::print).

Disclaimer: I'm the author of {fmt} and C++20 std::format.