7

I am running into a formatting issue on floating-point values, in the sense of returning to "default formatting". Say I have 2 floats:

float f1 = 3.0f, f2 = 1.5f;
std::cout << f1 << " - " << f2 << "\n";

will show these as: 3 - 1.5

Now, for some reason, I need to set precision on std::cout (for other printing):

cout << std::precision(2);

If I print again my two floats, this will lead to: 3.00 - 1.50

Now I want to get back to default formatting. Until C++11 this seems to be difficult (or was it ?). But, thanks, I now got this new flag: std::defaultfloat. Lets try:

std::cout << std::defaultfloat << f1 << " - " << f2;

will print: 3 - 1.50. Fine.

Oh, but wait. Say I have instead:

float f1 = 444.0f, f2 = 444.5f;

Default printing will show: 444 - 444.5

Setting precision (and "fixed"):

cout << std::precision(2) << std::fixed;

will show: 444.00 - 444.50

But returning to "default":

std::cout << std::defaultfloat << f1 << " - " << f2;

will print: 4.4e+02 - 4.4e+02 (automatic switching to scientific format). And, in case you wonder, appending the "fixed" flag will keep the previously assigned precision, thus not returning back to original setting.

Question: How do I get back to the default mode ?

FWIW, live code is here.

Edit: this question has been tagged as a dupe but the linked answer does not provide an answer to the question, it only mentions how to get the current precision.

Edit 2: upon request, here is the full code demonstrating the issue:

int main()
{
    float f1 = 444.5f, f2=443.0f;
    std::cout << f1 << " - " << f2 << "\n";
    std::cout << std::fixed << std::setprecision(2);
    std::cout << f1 << " - " << f2 << "\n";
    std::cout << std::defaultfloat;
    std::cout << f1 << " - " << f2 << "\n";
}

And the result:

444.5 - 443
444.50 - 443.00
4.4e+02 - 4.4e+02
kebs
  • 6,387
  • 4
  • 41
  • 70
  • 1
    I'm not precisely sure why you've having this problem, but one solution which has existed since before C++11 and would still work now is Boost's I/O Stream State Saver library: https://www.boost.org/doc/libs/1_68_0/libs/io/doc/ios_state.html – John Zwinck Oct 28 '18 at 11:36
  • Thanks! Didn't know that one (so many things in Boost...), I'll give a try. But shoudn't this be possible with standard library? Or, at least, assigning a setting saying "print like an integer if fractional part is 0"? – kebs Oct 28 '18 at 11:40
  • @JohnZwinck ok, checked the other answer. However, it doesn't really answer the question here (although it gave me a hint). Could you please reopen so I can add my own answer I have put up in the meanwhile. – kebs Oct 28 '18 at 15:09
  • It would improve the question to have the [MCVE](https://stackoverflow.com/help/mcve) showing the undesirable behaviour in the question, instead of a series of snippets and/or an external MCVE link. Also you don't need to show the cases that work correctly. – M.M Oct 29 '18 at 03:41
  • @M.M Correct, I edited the question. – kebs Oct 29 '18 at 07:52

4 Answers4

5

In C++20 you can use std::format which is a stateless API. In particular, specifying the precision in one call won't affect the other:

float f1 = 444.0f, f2 = 444.5f;
std::cout << std::format("{} - {}\n", f1, f2);
// Output: 444 - 444.5
std::cout << std::format("{:.2f} - {:.2f}\n", f1, f2);
// Output: 444.00 - 444.50
std::cout << std::format("{} - {}\n", f1, f2);
// Output: 444 - 444.5

std::format is not widely available yet but you can use the {fmt} library, std::format is based on, in the meantime. It also provides a print function that combines formatting and I/O:

float f1 = 444.0f, f2 = 444.5f;
fmt::print("{} - {}\n", f1, f2);
// Output: 444 - 444.5
fmt::print("{:.2f} - {:.2f}\n", f1, f2);
// Output: 444.00 - 444.50
fmt::print("{} - {}\n", f1, f2);
// Output: 444 - 444.5

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

vitaut
  • 49,672
  • 25
  • 199
  • 336
  • Thanks for this answer. Yes, I've seen that but I'm not a fan. This paradigm (embbeding the object to print in a function call) really sounds to me like going back to C (`printf(`...), although I understand it is much better than that, and that people might adopt it. I really like streams, they are so convenient. But anyway, thank for that contribution ! – kebs May 20 '21 at 10:12
2

std::defaultfloat doesn't reset the precision. (Don't ask me why). You could reset that to the default which is defined as 6:

std::cout << std::defaultfloat << std::setprecision(6) << f1 << " - " << f2;

Alternatively you could save the entire stream state before the operation and restore it after; see this thread for that.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Yep, that's it. The thing that was unclear for me is that in "default" mode (that is, after streaming `defaultfloat`), you need to set the precision to a number of digits **larger** than the total number of digits of the number to avoid the dot. – kebs Oct 29 '18 at 07:49
1

std::precision specifies the precision at which cout displays, it doesn't change when you change the formating.

So when you set std::precision(2) it specifies that all formats following will display with that precision applied.

The format fixed applies precision as n being the number of places after the decimal point. The defaultfloat format applies precision as the maximum number of total digits to display.

It DOESN'T reset the precision value.

For reference: ios_base::precision

  • Yes, I got that, thanks . But... you have any idea on my question? (or a way to set the original format). – kebs Oct 28 '18 at 11:52
  • Have a look at the reference for precision. It does show you how to get the current precision, so you should be able to figure out how to get it pretty easily. But just as a shortcut, the default precision value is 6. – Roderick Lenz Oct 28 '18 at 13:10
0

You can simply create your own std::ostream over the std::streambuf of the original std::ostream, and set your formatting on this new personal std::ostream.

#include <iostream>
#include <ostream>
#include <iomanip>

float const f1 = 3.0f, f2 = 1.5f;

void proc1() {
    std::ostream out(std::cout.rdbuf());
    out << "Inside proc1(), floats are printed like: " << std::setprecision(2) << std::fixed << f1 << " and " << f2 << '\n';
}

int main() {
    std::cout << "Inside main(), floats are printed like: " << f1 << " and " << f2 << '\n';
    proc1();
    std::cout << "Back in main(), floats are printed like: " << f1 << " and " << f2 << '\n';
}

This code will print:

Inside main(), floats are printed like: 3 and 1.5
Inside proc1(), floats are printed like: 3.00 and 1.50
Back in main(), floats are printed like: 3 and 1.5

You can try it there: http://coliru.stacked-crooked.com/a/4994c3f604b63d67

It is correct to create an std::ostream based on the std::streambuf of another one because the destructor of std::ostream doesn't touch its assigned std::streambuf. See: https://en.cppreference.com/w/cpp/io/basic_ostream/%7Ebasic_ostream

fjardon
  • 7,921
  • 22
  • 31
  • Thks, nice answer! Yes, true, you can always duplicate the stream and assign specific formatting to it; – kebs May 20 '21 at 17:05