格式化浮点数:返回默认值

7

我遇到了一个浮点数格式的问题,想要返回“默认格式”。 假设我有两个浮点数:

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

将显示为:3 - 1.5

现在,由于某种原因,我需要在std::cout上设置精度(用于其他打印):

cout << std::precision(2);

如果我再次打印我的两个浮点数,结果将是:3.00 - 1.50。
现在我想要恢复默认格式。在C++11之前,这似乎很困难(或者是吗?)。但是幸好我现在有了新的标志:std::defaultfloat。让我们试试:
std::cout << std::defaultfloat << f1 << " - " << f2;

将会输出: 3 - 1.50。好的。

不过,等等。假设我有以下代码:

float f1 = 444.0f, f2 = 444.5f;

默认打印将显示:444-444.5

设置精度(和“fixed”):

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

将显示:444.00-444.50

但回到“默认”:

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

将打印:4.4e+02 - 4.4e+02(自动切换到科学格式)。如果你想知道,附加“fixed”标志将保持之前分配的精度,因此不会返回原始设置。

问题: 如何回到默认模式?

FWIW,live code is here

Edit: 这个问题被标记为重复了,但是linked answer并没有提供答案,它只提到了如何获得当前精度。

Edit 2: 应请求,这里是演示问题的完整代码:

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";
}

结果如下:

444.5 - 443
444.50 - 443.00
4.4e+02 - 4.4e+02

1
我不确定为什么你会遇到这个问题,但是一个解决方案是使用 Boost 的 I/O Stream State Saver 库,它在 C++11 之前就存在,并且现在仍然适用:https://www.boost.org/doc/libs/1_68_0/libs/io/doc/ios_state.html。 - John Zwinck
谢谢!我不知道这个(Boost库有很多东西...),我会试一下。但是这不应该可以用标准库实现吗?或者至少可以设置一个选项说“如果小数部分为0,则打印为整数”? - kebs
@JohnZwinck 好的,我看了另一个答案。然而,它并没有真正回答这个问题(虽然它给了我一个提示)。你能否重新打开,以便我可以添加我自己的答案,我已经在此期间发布了。 - kebs
MCVE 显示在问题中,而不是一系列片段和/或外部 MCVE 链接,可以改善问题。此外,您无需显示正常工作的情况。 - M.M
@M.M 正确,我编辑了问题。 - kebs
4个回答

5
在C++20中,你可以使用std::format,这是一个无状态的API。特别地,在一个调用中指定精度不会影响其他调用:
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目前还不是很普遍,但是您可以在此期间使用基于它的{fmt}库。它还提供了一个将格式化和I/O结合起来的print函数:

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

免责声明:本人是{fmt}和C++20的std::format的作者。


谢谢你的回答。是的,我看到了,但我不是粉丝。这种范式(将要打印的对象嵌入函数调用中)对我来说真的听起来像回到C语言(printf(...),尽管我知道它比那好得多,并且人们可能会采用它。我真的很喜欢流,它们非常方便。但无论如何,感谢您的贡献! - kebs

2

std::defaultfloat不会重置精度。(别问我为什么)。你可以将其重置为默认值,其定义为6:

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

或者你可以在操作之前保存整个流状态,然后在操作之后恢复它;请参见此线程


没错,就是这样。让我感到困惑的是,在“默认”模式下(也就是在流defaultfloat之后),你需要将精度设置为比数字的总位数的数字,以避免出现小数点。 - kebs

1

std::precision指定cout显示的精度,它不会随着格式更改而改变。

因此,当您设置std::precision(2)时,它指定所有后续格式都将应用该精度进行显示。

fixed格式将n作为小数点后的位数应用精度。 默认浮点格式将精度应用为要显示的最大总位数。

它不会重置精度值。

参考文献: ios_base :: precision


是的,我明白了,谢谢。但是...你有任何关于我的问题的想法吗?(或者有一种设置原始格式的方法吗?) - kebs
请查看精度的参考文献。 它会展示如何获取当前精度,因此您应该能够很容易地找出如何获取它。但是作为一种快捷方式,默认精度值为6。 - Roderick Lenz

0
您可以通过在原始 std::ostreamstd::streambuf 上创建自己的 std::ostream,并在这个新的个人 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';
}

这段代码将会输出:
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

您可以在这里尝试:http://coliru.stacked-crooked.com/a/4994c3f604b63d67

根据std::ostream的析构函数不会触及其分配的std::streambuf,因此基于另一个std::streambuf创建std::ostream是正确的。请参见:https://en.cppreference.com/w/cpp/io/basic_ostream/%7Ebasic_ostream


谢谢,好答案!是的,你总是可以复制流并为其分配特定的格式。 - kebs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接