std::setprecision 设置有效数字的个数。如何使用 iomanip 设置精度?
std::setprecision sets the number of significant figures. How do I use iomanip to set the precision?
我一直觉得 iomanip 令人困惑且违反直觉。我需要帮助。
快速互联网搜索发现 (https://www.vedantu.com/maths/precision)“因此,我们将精度视为小数点后 小数点后 的最大有效数字位数”(重点是我的)。这也符合我的理解。但是我写了一个测试程序并且:
stm << std::setprecision(3) << 5.12345678;
std::cout << "5.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 25.12345678;
std::cout << "25.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 5.1;
std::cout << "5.1: " << stm.str() << std::endl;
stm.str("");
输出:
5.12345678: 5.12
25.12345678: 25.1
5.1: 5.1
如果精度为 3,则输出应为:
5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1
显然,C++ 标准对与浮点数相关的“精度”的含义有不同的解释。
如果我这样做:
stm.setf(std::ios::fixed, std::ios::floatfield);
那么前两个值的格式正确,但最后一个值显示为 5.100
。
如何在没有填充的情况下设置精度?
我认为你做的不好。有两种候选格式:defaultfloat
和fixed
。对于前者,“精度”是最大位数,其中小数点分隔符的两边计数。对于后者,“精度”是小数点分隔符 后 的确切位数。
所以我认为您的解决方案是使用 fixed
格式,然后手动清除尾随零:
#include <iostream>
#include <iomanip>
#include <sstream>
void print(const double number)
{
std::ostringstream stream;
stream << std::fixed << std::setprecision(3) << number;
auto string=stream.str();
while(string.back()=='0')
string.pop_back();
if(string.back()=='.') // in case number is integral; beware of localization issues
string.pop_back();
std::cout << string << "\n";
}
int main()
{
print(5.12345678);
print(25.12345678);
print(5.1);
}
您可以尝试使用此解决方法:
decltype(std::setprecision(1)) setp(double number, int p) {
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0 && e > 1)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
return std::setprecision(e);
}
然后:
auto v = 5.12345678;
stm << setp(v, 3) << v;
另一个更冗长和优雅的解决方案是创建一个像这样的结构:
struct __setp {
double number;
bool fixed = false;
int prec;
};
std::ostream& operator<<(std::ostream& os, const __setp& obj)
{
if(obj.fixed)
os << std::fixed;
else os << std::defaultfloat;
os.precision(obj.prec);
os << obj.number; // comment this if you do not want to print immediately the number
return os;
}
__setp setp(double number, int p) {
__setp setter;
setter.number = number;
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
if(e <= 0) {
setter.fixed = true;
setter.prec = 1;
} else
setter.prec = e;
return setter;
}
这样使用:
auto v = 5.12345678;
stm << setp(v, 3);
除了保留尾随零之外,固定格式几乎可以满足您的需求。没有内置方法可以避免这种情况,但您可以轻松地手动删除这些零。例如,在 C++20 中,您可以使用 std::format
:
执行以下操作
std::string format_fixed(double d) {
auto s = fmt::format("{:.3f}", d);
auto end = s.find_last_not_of('0');
return end != std::string::npos ? std::string(s.c_str(), end + 1) : s;
}
std::cout << "5.12345678: " << format_fixed(5.12345678) << "\n";
std::cout << "25.12345678: " << format_fixed(25.12345678) << "\n";
std::cout << "5.1: " << format_fixed(5.1) << "\n";
输出:
5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1
与the {fmt} library, std::format
is based on: godbolt相同的示例。
免责声明:我是 {fmt} 和 C++20 的作者 std::format
。
我一直觉得 iomanip 令人困惑且违反直觉。我需要帮助。
快速互联网搜索发现 (https://www.vedantu.com/maths/precision)“因此,我们将精度视为小数点后 小数点后 的最大有效数字位数”(重点是我的)。这也符合我的理解。但是我写了一个测试程序并且:
stm << std::setprecision(3) << 5.12345678;
std::cout << "5.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 25.12345678;
std::cout << "25.12345678: " << stm.str() << std::endl;
stm.str("");
stm << std::setprecision(3) << 5.1;
std::cout << "5.1: " << stm.str() << std::endl;
stm.str("");
输出:
5.12345678: 5.12
25.12345678: 25.1
5.1: 5.1
如果精度为 3,则输出应为:
5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1
显然,C++ 标准对与浮点数相关的“精度”的含义有不同的解释。
如果我这样做:
stm.setf(std::ios::fixed, std::ios::floatfield);
那么前两个值的格式正确,但最后一个值显示为 5.100
。
如何在没有填充的情况下设置精度?
我认为你做的不好。有两种候选格式:defaultfloat
和fixed
。对于前者,“精度”是最大位数,其中小数点分隔符的两边计数。对于后者,“精度”是小数点分隔符 后 的确切位数。
所以我认为您的解决方案是使用 fixed
格式,然后手动清除尾随零:
#include <iostream>
#include <iomanip>
#include <sstream>
void print(const double number)
{
std::ostringstream stream;
stream << std::fixed << std::setprecision(3) << number;
auto string=stream.str();
while(string.back()=='0')
string.pop_back();
if(string.back()=='.') // in case number is integral; beware of localization issues
string.pop_back();
std::cout << string << "\n";
}
int main()
{
print(5.12345678);
print(25.12345678);
print(5.1);
}
您可以尝试使用此解决方法:
decltype(std::setprecision(1)) setp(double number, int p) {
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0 && e > 1)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
return std::setprecision(e);
}
然后:
auto v = 5.12345678;
stm << setp(v, 3) << v;
另一个更冗长和优雅的解决方案是创建一个像这样的结构:
struct __setp {
double number;
bool fixed = false;
int prec;
};
std::ostream& operator<<(std::ostream& os, const __setp& obj)
{
if(obj.fixed)
os << std::fixed;
else os << std::defaultfloat;
os.precision(obj.prec);
os << obj.number; // comment this if you do not want to print immediately the number
return os;
}
__setp setp(double number, int p) {
__setp setter;
setter.number = number;
int e = static_cast<int>(std::abs(number));
e = e != 0? static_cast<int>(std::log10(e)) + 1 + p : p;
while(number != 0.0 && static_cast<int>(number*=10) == 0)
e--; // for numbers like 0.001: those zeros are not treated as digits by setprecision.
if(e <= 0) {
setter.fixed = true;
setter.prec = 1;
} else
setter.prec = e;
return setter;
}
这样使用:
auto v = 5.12345678;
stm << setp(v, 3);
除了保留尾随零之外,固定格式几乎可以满足您的需求。没有内置方法可以避免这种情况,但您可以轻松地手动删除这些零。例如,在 C++20 中,您可以使用 std::format
:
std::string format_fixed(double d) {
auto s = fmt::format("{:.3f}", d);
auto end = s.find_last_not_of('0');
return end != std::string::npos ? std::string(s.c_str(), end + 1) : s;
}
std::cout << "5.12345678: " << format_fixed(5.12345678) << "\n";
std::cout << "25.12345678: " << format_fixed(25.12345678) << "\n";
std::cout << "5.1: " << format_fixed(5.1) << "\n";
输出:
5.12345678: 5.123
25.12345678: 25.123
5.1: 5.1
与the {fmt} library, std::format
is based on: godbolt相同的示例。
免责声明:我是 {fmt} 和 C++20 的作者 std::format
。