Formatting floats: returning to default
Asked Answered
C

4

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
Cupro answered 28/10, 2018 at 11:9 Comment(5)
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: boost.org/doc/libs/1_68_0/libs/io/doc/ios_state.htmlCheesecake
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"?Cupro
@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.Cupro
It would improve the question to have the 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.Tawnyatawsha
@Tawnyatawsha Correct, I edited the question.Cupro
T
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.

Tawnyatawsha answered 29/10, 2018 at 3:39 Comment(1)
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.Cupro
L
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.

Langrage answered 19/5, 2021 at 22:51 Comment(1)
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 !Cupro
T
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.

Tawnyatawsha answered 29/10, 2018 at 3:39 Comment(1)
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.Cupro
P
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

Poulos answered 28/10, 2018 at 11:48 Comment(2)
Yes, I got that, thanks . But... you have any idea on my question? (or a way to set the original format).Cupro
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.Poulos
T
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

Tease answered 20/5, 2021 at 16:56 Comment(1)
Thks, nice answer! Yes, true, you can always duplicate the stream and assign specific formatting to it;Cupro

© 2022 - 2024 — McMap. All rights reserved.