我想输出一个浮点数作为百分比,最多三位小数。
我知道iostream有三种不同的呈现浮动的方式:
“默认”,使用fixed
或scientific
的规则显示,具体取决于setprecision
定义的有效位数;
fixed
,显示由setprecision
定义的固定小数位数;以及
scientific
,显示固定数量的小数位,但使用科学记数法,即基数的尾数+指数。
使用以下代码,这三种模式可以是seen in effect:
#include <iostream>
#include <iomanip>
int main() {
double d = 0.00000095;
double e = 0.95;
std::cout << std::setprecision(3);
std::cout.unsetf(std::ios::floatfield);
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
std::cout << std::fixed;
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
std::cout << std::scientific;
std::cout << "d = " << (100. * d) << "%\n";
std::cout << "e = " << (100. * e) << "%\n";
}
// output:
// d = 9.5e-05%
// e = 95%
// d = 0.000%
// e = 95.000%
// d = 9.500e-05%
// e = 9.500e+01%
这些选项都不符合我的要求。
我想在这里避免使用任何科学记法,因为它使百分比真的难以阅读。我想保留最多三个小数位,如果非常小的值显示为零,则可以。但是,我还想避免在上面的0.95这样的情况下在小数位上尾随零:我想要在第二行显示为“95%”。
在.NET中,我可以使用自定义格式字符串来实现这一点,例如“0。###%”,它给出了一个格式为百分比的数字,小数点分隔符的左边至少有一位数字,最多小数点分隔符右边三位数,跳过尾随零:http://ideone.com/uV3nDi
我可以使用iostream实现这一点,而无需编写自己的格式化逻辑(例如特殊的小数字套管)吗?
答案 0 :(得分:4)
我有理由确定iostreams内置的任何东西都不能直接支持。
我认为处理它的最简洁的方法是在将数字传递给iostream之前将其舍入:
#include <iostream>
#include <vector>
#include <cmath>
double rounded(double in, int places) {
double factor = std::pow(10, places);
return std::round(in * factor) / factor;
}
int main() {
std::vector<double> values{ 0.000000095123, 0.0095123, 0.95, 0.95123 };
for (auto i : values)
std::cout << "value = " << 100. * rounded(i, 5) << "%\n";
}
由于它进行舍入的方式,这对它可以使用的数字的大小有限制。对于百分比,这可能不是问题,但 if 你正在处理一个接近最大值的数字,可以在有问题的类型中表示(在这种情况下为double
)乘法pow(10, places)
可能/会溢出并产生不良结果。
虽然我不能绝对确定,但这似乎不会导致您似乎试图解决的问题出现问题。
答案 1 :(得分:3)
我很认真。我不喜欢它。它可能很慢,而且函数有一个愚蠢的名字。也许你可以用它进行测试验证,因为它太笨了我想你很容易看到它几乎必须工作。
它还假设小数点分隔符为'.'
,但不一定是这种情况。适当的点可以通过以下方式获得:
char point = std::use_facet< std::numpunct<char> >(std::cout.getloc()).decimal_point();
但是仍然没有解决问题,因为用于数字的字符可能不同,一般而言,这不应该以这种方式编写。
template<typename Floating>
std::string formatFloatingUpToN(unsigned n, Floating f) {
std::stringstream out;
out << std::setprecision(n) << std::fixed;
out << f;
std::string ret = out.str();
// if this clause holds, it's all zeroes
if (std::abs(f) < std::pow(0.1, n))
return ret;
while (true) {
if (ret.back() == '0') {
ret.pop_back();
continue;
} else if (ret.back() == '.') {
ret.pop_back();
break;
} else
break;
}
return ret;
}