提问人:Jason Punyon 提问时间:2/17/2009 最后编辑:vitautJason Punyon 更新时间:6/10/2023 访问量:682663
如何使用 cout 全精度打印双精度值?
How do I print a double value with full precision using cout?
答:
#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
评论
std::setprecision (17)
最便携...
#include <limits>
using std::numeric_limits;
...
cout.precision(numeric_limits<double>::digits10 + 1);
cout << d;
评论
这是我将使用的:
std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< 3.14159265358979
<< std::endl;
基本上,限制包具有所有内置类型的特征。
浮点数(浮点数/双精度数/长双数)的特征之一是 digits10 属性。这定义了以 10 为基数的浮点数的准确性(我忘记了确切的术语)。
有关其他属性的详细信息,请参见:http://www.cplusplus.com/reference/std/limits/numeric_limits.html
。
评论
std::setprecision()
#include <iomanip>
std::numeric_limits<double>
numberic_limits<double>
1
std::numeric_limits<double>::digits10
max_digits10
digits10+2
float
long double
boost::multiprecision::float128
+3
+2
使用 ostream::p recision(int)
cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;
将屈服
3.141592653589793, 2.718281828459045
为什么你必须说“+1”我不知道,但你从中得到的额外数字是正确的。
评论
printf("%.12f", M_PI);
%.12f 表示浮点数,精度为 12 位。
评论
iostreams 的方式有点笨拙。我更喜欢使用 boost::lexical_cast
,因为它可以为我计算正确的精度。而且速度也很快。
#include <string>
#include <boost/lexical_cast.hpp>
using boost::lexical_cast;
using std::string;
double d = 3.14159265358979;
cout << "Pi: " << lexical_cast<string>(d) << endl;
输出:
圆周率:3.14159265358979
评论
double
double
通过完全精度,我假设平均精度足以显示对预期值的最佳近似值,但应该指出的是,使用基数 2 表示进行存储,并且基数 2 不能完全表示微不足道的东西。获得实际双精度(无四舍五入误差)的唯一方法是打印出二进制位(或十六进制 nybble)。double
1.1
一种方法是使用 a 将 键入双关语为整数,然后打印整数,因为整数不会出现截断或舍入问题。(C++ 标准不支持这样的类型双关语,但在 C 中支持。但是,大多数 C++ 编译器无论如何都可能会打印出正确的值。我认为 g++ 支持这一点。union
double
union {
double d;
uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;
这将为您提供双精度的 100% 准确精度。并且完全不可读,因为人类无法阅读IEEE双格式!维基百科上有一篇关于如何解释二进制位的好文章。
在较新的 C++ 中,您可以执行以下操作
std::cout << std::hexfloat << 1.1;
评论
x.u64
以下是如何全精度显示双精度:
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;
这将显示:
100.0000000000005
max_digits10 是唯一表示所有不同双精度值所需的位数。max_digits10表示小数点前后的位数。
不要将 set_precision(max_digits10) 与 std::fixed 一起使用。
在固定表示法中,set_precision() 仅设置小数点之后的位数。这是不正确的max_digits10因为它表示小数点前后的位数。
double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;
这将显示不正确的结果:
100.00000000000049738
注意:需要头文件
#include <iomanip>
#include <limits>
评论
100.0000000000005
double
100.0000000000005 - 100
4.973799150320701e-13
如何使用 cout 全精度打印值?
double
使用或使用
并设置精度hexfloat
scientific
std::cout.precision(std::numeric_limits<double>::max_digits10 - 1);
std::cout << std::scientific << 1.0/7.0 << '\n';
// C++11 Typical output
1.4285714285714285e-01
太多的答案只涉及以下一项:1)基础,2)固定/科学的布局或3)精确性。太多的精确答案无法提供所需的适当价值。因此,这是对一个老问题的回答。
- 什么基础?
A 肯定是使用基数 2 编码的。C++11 的直接方法是使用 .
如果非十进制输出是可以接受的,我们就完成了。double
std::hexfloat
std::cout << "hexfloat: " << std::hexfloat << exp (-100) << '\n';
std::cout << "hexfloat: " << std::hexfloat << exp (+100) << '\n';
// output
hexfloat: 0x1.a8c1f14e2af5dp-145
hexfloat: 0x1.3494a9b171bf5p+144
- 否则: 或 ?
fixed
scientific
A 是浮点类型,而不是定点。double
不要使用,因为这样无法打印出小的任何东西。对于大,它打印许多数字,也许是数百个可疑的信息。std::fixed
double
0.000...000
double
std::cout << "std::fixed: " << std::fixed << exp (-100) << '\n';
std::cout << "std::fixed: " << std::fixed << exp (+100) << '\n';
// output
std::fixed: 0.000000
std::fixed: 26881171418161356094253400435962903554686976.000000
要全精度打印,首先使用它将“用科学记数法写入浮点值”。请注意,小数点后 6 位的默认值(数量不足)将在下一个点处理。std::scientific
std::cout << "std::scientific: " << std::scientific << exp (-100) << '\n';
std::cout << "std::scientific: " << std::scientific << exp (+100) << '\n';
// output
std::scientific: 3.720076e-44
std::scientific: 2.688117e+43
- 精度是多少(总位数)?
使用二进制基数 2 编码的编码在 2 的各种幂之间编码相同的精度。这通常是 53 位。double
[1.0...2.0) 有 2 53 个不同,[2.0...4.0) 有 2 53 个不同,[4.0...8.0) 有 2 53 个不同,
[8.0...10.0) 有 2/8 * 2 53 个不同。double
double
double
double
然而,如果代码以十进制打印,并带有有效数字,则组合数 [1.0...10.0) 为 9/10 * 10N。N
无论选择何种 N
(精度),双精度
文本和十进制文本之间都不会有一对一的映射。如果选择固定值,有时它会略高于或低于某些值的实际需要。我们可能会犯错,因为太少(下面)或太多(下面)。N
double
a)
b)
3名候选人:N
a) 从文本转换时使用 so--text 我们得出所有文本的相同文本。N
double
double
std::cout << dbl::digits10 << '\n';
// Typical output
15
b) 从 -text- 转换时使用 so-,我们得出的结论是相同的。N
double
double
double
double
// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17
当不可用时,请注意,由于以 2 为基数和以 10 为基数的属性,我们可以用来确保打印足够的十进制数字。max_digits10
digits10 + 2 <= max_digits10 <= digits10 + 3
digits10 + 3
c) 使用随值变化的 an。N
当代码想要显示最小文本 () 或 a 的确切值 (在 的情况下为 )时,这可能很有用。然而,由于这是“工作”,不太可能是OP的目标,因此它将被搁置一旁。N == 1
double
N == 1000-ish
denorm_min
它通常用于 b) 用于“以全精度打印值”。一些应用程序可能更喜欢 a) 错误,因为没有提供太多信息。double
使用 ,设置要在小数点后打印的位数,以便打印数字。代码需要总位数,因此用 ..scientific
.precision()
1 + .precision()
max_digits10
.precision()
max_digits10 - 1
typedef std::numeric_limits< double > dbl;
std::cout.precision(dbl::max_digits10 - 1);
std::cout << std::scientific << exp (-100) << '\n';
std::cout << std::scientific << exp (+100) << '\n';
// Typical output
3.7200759760208361e-44
2.6881171418161356e+43
//2345678901234567 17 total digits
评论
precision()
scientific
cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"
printf
char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);
std::cout <<
std::cout.precision(n)
这将在点后最多显示两位小数位的值。
#include <iostream>
#include <iomanip>
double d = 2.0;
int n = 2;
cout << fixed << setprecision(n) << d;
请参阅此处:定点符号
使用固定浮点表示法 设置 floatfield 格式标志 将 str 流固定。
当 floatfield 设置为 fixed 时,将写入浮点值 使用定点表示法:该值用 精度字段指定的小数部分中的许多数字 (精度)并且没有指数部分。
设置十进制精度 设置用于格式化的十进制精度 输出操作的浮点值。
如果您熟悉表示浮点的IEEE标准,您就会知道不可能在标准范围之外以全精度显示浮点,也就是说,它总是会导致实际值的四舍五入。
您需要首先检查该值是否在范围内,如果是,则使用:
cout << defaultfloat << d ;
使用默认浮点表示法 设置 floatfield 格式标志 将 str 流设置为 defaultfloat。
当 floatfield 设置为 defaultfloat 时,浮点值为 使用默认表示法编写:表示法使用尽可能多的表示法 根据需要提供有意义的数字,最高可达流的十进制精度 (精度),计算小数点前后的数字 点(如果有)。
这也是 的默认行为,这意味着您不会显式使用它。cout
评论
这是一个适用于任何浮点类型的函数,不仅适用于 ,还可以将流恢复到之后找到的方式。不幸的是,它不会与线程很好地交互,但这就是 iostreams 的本质。在文件开头需要这些内容:double
#include <limits>
#include <iostream>
这是函数,如果你经常使用它,你可以把它放在头文件中:
template <class T>
void printVal(std::ostream& os, T val)
{
auto oldFlags = os.flags();
auto oldPrecision = os.precision();
os.flags(oldFlags & ~std::ios_base::floatfield);
os.precision(std::numeric_limits<T>::digits10);
os << val;
os.flags(oldFlags);
os.precision(oldPrecision);
}
像这样使用它:
double d = foo();
float f = bar();
printVal(std::cout, d);
printVal(std::cout, f);
如果您希望能够使用普通的插入运算符,则可以使用以下额外的包装器代码:<<
template <class T>
struct PrintValWrapper { T val; };
template <class T>
std::ostream& operator<<(std::ostream& os, PrintValWrapper<T> pvw) {
printVal(os, pvw.val);
return os;
}
template <class T>
PrintValWrapper<T> printIt(T val) {
return PrintValWrapper<T>{val};
}
现在你可以像这样使用它:
double d = foo();
float f = bar();
std::cout << "The values are: " << printIt(d) << ", " << printIt(f) << '\n';
IEEE 754 浮点值使用以 2 为基数的表示进行存储。任何以 2 为基数的数字都可以表示为十进制(以 10 为基数)到全精度。然而,提出的答案都没有。它们都会截断十进制值。
这似乎是由于对 std::numeric_limits<T>::max_digits10
所代表的含义的误解:
的值是以 10 为基数的位数,这些位数是唯一表示该类型的所有非重复值所必需的。
std::numeric_limits<T>::max_digits10
T
换句话说:如果您想在不丢失任何信息的情况下从二进制到十进制再到二进制,这是输出所需的(最坏情况)位数。如果至少输出小数并重建浮点值,则可以保证获得与开始时完全相同的二进制表示形式。max_digits10
重要的是:一般来说,它既不产生最短的小数点,也不足以表示完整的精度。我不知道 C++ 标准库中有一个常量对包含浮点值的完整精度所需的最大十进制位数进行编码。我相信它类似于 s767 的 1。输出具有全精度的浮点值的一种方法是使用足够大的精度值,如 2,并让库去除任何尾随零:max_digits10
double
#include <iostream>
int main() {
double d = 0.1;
std::cout.precision(767);
std::cout << "d = " << d << std::endl;
}
这将生成以下输出,其中包含完整的精度:
d = 0.1000000000000000055511151231257827021181583404541015625
请注意,它的小数点比建议的要多得多。max_digits10
虽然这回答了所提出的问题,但一个更常见的目标是获得任何给定浮点值的最短十进制表示,并保留所有信息。同样,我不知道有任何方法可以指示标准 I/O 库输出该值。从 C++17 开始,进行这种转换的可能性终于以 std::to_chars
的形式出现在C++中。默认情况下,它会生成保留整个信息的任何给定浮点值的最短十进制表示形式。
它的界面有点笨拙,您可能希望将其包装成一个函数模板,该模板返回您可以输出到的东西(例如 ),例如std::cout
std::string
#include <charconv>
#include <array>
#include <string>
#include <system_error>
#include <iostream>
#include <cmath>
template<typename T>
std::string to_string(T value)
{
// 24 characters is the longest decimal representation of any double value
std::array<char, 24> buffer {};
auto const res { std::to_chars(buffer.data(), buffer.data() + buffer.size(), value) };
if (res.ec == std::errc {})
{
// Success
return std::string(buffer.data(), res.ptr);
}
// Error
return { "FAILED!" };
}
int main()
{
auto value { 0.1f };
std::cout << to_string(value) << std::endl;
value = std::nextafter(value, INFINITY);
std::cout << to_string(value) << std::endl;
value = std::nextafter(value, INFINITY);
std::cout << to_string(value) << std::endl;
}
这将打印出来(使用 Microsoft 的 C++ 标准库):
0.1
0.10000001
0.10000002
1 摘自 Stephan T. Lavavej 在 CppCon 2019 上题为“浮点<charconv>:使用 C++17 的最终 Boss 使代码速度提高 10 倍”的演讲。(整个演讲值得一看。
2 这也需要使用科学
和固定
的组合,以较短者为准。我不知道使用 C++ 标准 I/O 库设置此模式的方法。
评论
DBL_TRUE_MIN
DBL_TRUE_MIN
long double::max_digits10
DBL_MIN - DBL_TRUE_MIN
C++20 标准::格式
这个伟大的新 C++ 库功能的优点是不会影响 的状态:std::cout
std::setprecision
#include <format>
#include <string>
int main() {
std::cout << std::format("{:.2} {:.3}\n", 3.1415, 3.1415);
}
预期输出:
3.14 3.142
如 https://stackoverflow.com/a/65329803/895245 所述,如果不显式传递精度,它将打印具有往返保证的最短十进制表示形式。TODO更详细地了解它与以下各项的比较: 如 https://stackoverflow.com/a/554134/895245 所示:dbl::max_digits10
{:.{}}
#include <format>
#include <limits>
#include <string>
int main() {
std::cout << std::format("{:.{}}\n",
3.1415926535897932384626433, dbl::max_digits10);
}
另请参阅:
- 在 C++ 中设置默认浮点打印精度,以便在 c++20 之前恢复初始精度
- std::string 格式,如 sprintf
- https://en.cppreference.com/w/cpp/utility/format/formatter#Standard_format_specification
在 C++20 中,您可以使用 std::format
来执行此操作:
std::cout << std::format("{}", std::numbers::pi_v<double>);
输出(假设 IEEE 754 ):double
3.141592653589793
默认浮点格式是具有往返保证的最短十进制表示形式。与 I/O 操纵器相比,此方法的优点是它不会打印不必要的数字,并且不受全局状态的影响(有关详细信息,请参阅此博客文章)。setprecision
同时,您可以使用 {fmt} 库,是基于的。{fmt} 还提供了使这更简单、更高效的功能 (godbolt):std::format
print
fmt::print("{}", M_PI);
这个问题实际上并没有定义“全精度”的含义。通常,它被理解为足以通过十进制进行往返的精度,但对于(有效)十进制数字的最大数量,还有另一种可能但不太可能的解释。对于 IEEE 754 double,后者为 767 位。
免责声明:我是{fmt}和C++20的作者。std::format
评论
std::format
在这个问题中,描述了如何无损地将双精度转换为字符串(在八度,但它可以在 C++ 中轻松复制)。De 想法是对浮点数有一个简短的人类可读描述和一个六边形的无损描述,例如:pi -> 3.14{54442d18400921fb}。
最好的选择是使用 ,解决方案的工作原理如下:std::setprecision
# include <iostream>
# include <iomanip>
int main()
{
double a = 34.34322;
std::cout<<std::fixed<<a<<std::setprecision(0)<<std::endl;
return 0;
}
注意:你不需要使用它来做这件事,我填了 0,因为它必须有一个参数。cout.setprecision
std::setprecision
评论
double pi = 3.141592653589793
3.141593
评论
cout << *reinterpret_cast<std::uint64_t *>(&my_double);