在我的之前的问题中,我使用cout
打印了一个double
,但结果被四舍五入了,而我并不希望它这样。如何让cout
以完整精度打印一个double
?
在我的之前的问题中,我使用cout
打印了一个double
,但结果被四舍五入了,而我并不希望它这样。如何让cout
以完整精度打印一个double
?
您可以直接在std::cout
上设置精度,并使用std::fixed
格式说明符。
double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;
您可以使用#include <limits>
来获取浮点数或双精度浮点数的最大精度。
#include <limits>
typedef std::numeric_limits< double > dbl;
double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
fixed
?对于double h = 6.62606957e-34;
,fixed
输出为0.000000000000000
,而scientific
则输出为6.626069570000000e-34
。 - Arthurcout.precision(numeric_limits<double>::digits10 + 2);
只能得到16位... - Assimilaterstd::format
来做到这一点。std::cout << std::format("{}", std::numbers::pi_v<double>);
double
):3.141592653589793
setprecision
I/O 操纵符相比,这种方法的优点是它不会打印不必要的数字,并且不受全局状态的影响(有关更多详细信息,请参阅 此博客文章)。std::format
。{fmt} 还提供了使其更加简单和高效的 print
函数(godbolt)。fmt::print("{}", M_PI);
std::format
的作者。#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
std::setprecision(17)
,请查看@Bill The Lizard的回答评论。 - Alec Jacobsonstd::setprecision
并不像人们想象的那样工作;例如,std::cout << std::setprecision(6) << 39927.887 << "\n";
只会打印出39927.9
。你真的应该提到这个参数指定了所有数字的数量,而不仅仅是小数点右边的数字。 - Philipp Ludwig我会使用以下代码:
std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
<< 3.14159265358979
<< std::endl;
基本上,limits包含了所有内置类型的特性。
浮点数(float/double/long double)之一的特性是 digits10 属性。该属性定义了一个十进制浮点数的精度(我忘记了确切的术语)。
有关其他属性的详细信息,请参见:http://www.cplusplus.com/reference/std/limits/numeric_limits.html。std::setprecision()
函数需要包含这个头文件:#include <iomanip>
。 - Martin Bergerstd::numeric_limits<double>
而不是numberic_limits<double>
。 - niklasfistd::numeric_limits<double>::digits10
上加 1
? - Alessandro Jacopson如何使用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)精度中的一个。 太多强调精度的答案未提供所需的正确值。 因此,这个回答是对一个老问题的回答。
double
肯定是用基数2编码的。 在C++11中,一种直接的方法是使用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
?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
为了获得完整的精度,请先使用 std::scientific
,它会“以科学计数法写入浮点值”。请注意默认情况下小数点后的6位数字是不足够的,这将在下一个步骤中处理。
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编码的double在各个2的幂之间编码相同的精度。通常为53位。
[1.0...2.0) 有2的53次方个不同的double,
[2.0...4.0) 有2的53次方个不同的double,
[4.0...8.0) 有2的53次方个不同的double,
[8.0...10.0) 有2/8 * 2的53次方个不同的double。
但是,如果用N
个有效数字将代码打印为十进制数,则组合数[1.0...10.0)为9/10 * 10的N次方。
无论选择什么样的N
(精度),都不会有一一对应的关系between double and decimal text。如果选择固定的N
,有时候对于某些double的值,它可能略微超过或低于真正需要的数量。我们可以出现过少错误(下面是a)
)或过多错误(下面是b)
)。
有3个候选的N
:
a) 使用一个N
,这样当从文本-double-文本进行转换时,所有double都可以得到相同的文本。
std::cout << dbl::digits10 << '\n';
// Typical output
15
b) 使用一个N
,这样在从double
-文本-double
进行转换时,我们可以确保对于所有double
,我们都会得到相同的double
。
// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17
当 max_digits10
不可用时,注意由于基数 2 和基数 10 属性,digits10 + 2 <= max_digits10 <= digits10 + 3
,我们可以使用 digits10 + 3
确保打印出足够的十进制数字。c)使用随值变化的 N
。
当代码想要显示最小文本(N == 1
)或者一个 double
的精确值(例如对于 denorm_min
,N == 大约1000
)时,这可能是有用的。然而,由于这是一项“工作”,不太可能是 OP 的目标,因此它将被搁置。
通常使用 b) 来“以完整精度打印 double
值”。一些应用程序可能更喜欢使用 a) 来避免提供过多信息而导致错误。
使用 .scientific
,.precision()
设置小数点后要打印的位数,因此将打印 1 + .precision()
位数字。代码需要 max_digits10
总数字,因此使用 max_digits10 - 1
调用 .precision()
。
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
将得到不同的结果。这些都是需要注意的令人困惑的地方。 - Simpletonchar buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);
额外的字符用于:符号、小数点、尾随零、e[+|-]、指数的3个数字(DBL_MAX_10_EXP = 308)。因此,所需的总字符数为25。 - Simpletonstd::cout <<
。只有极少数实现会对所有的std::cout.precision(n)
进行转换。IEEE 754仅要求至少dbl::max_digits10 + 3。由于“双精度和十进制文本之间的一一映射”指的是两个转换方向,我们同意其中一个方向可能存在映射,而另一个方向则不存在。 - chux - Reinstate Monicastd::scientific
,1 + setprecision
是打印的有效数字数。建议采用此答案的std::scientific
和cout.precision(dbl::max_digits10 - 1);
。 - chux - Reinstate Monica使用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
。要始终执行此操作,需要使用更多类似于max_digits10重要数字。 - chux - Reinstate Monica所谓完全精确,是指足够准确地显示所需值的最佳近似值,但应指出,double
使用基于2进制的表示法存储,而基于2进制不能准确表示像1.1
这样微不足道的东西。获取实际双精度浮点数(没有任何舍入误差)的完整精度的唯一方法是打印二进制位(或十六进制数字)。
一种方法是使用一个union
将double
强制转换为整数,然后打印该整数,因为整数不会遭受截断或舍入问题。 (像这样的类型强制转换不受 C++ 标准支持,但它在 C 中得到支持。 但是,大多数 C++ 编译器可能仍然会正确打印出该值。我认为 g++ 支持此操作。)
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;
以下是显示完整精度的 double 型变量的方法:
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表示小数点前后的数字位数。
不要在std::fixed中使用set_precision(max_digits10)。
对于定点表示法,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
。 - Evgeni SergeevC++20 std::format
这个伟大的 C++ 新库特性的优点是不会像 std::setprecision
一样影响到 std::cout
的状态:
#include <format>
#include <string>
int main() {
std::cout << std::format("{:.2} {:.3}\n", 3.1415, 3.1415);
}
期望输出:
3.14 3.142
如https://dev59.com/-nRB5IYBdhLWcg3wro2B#65329803所述,如果您没有明确传递精度,则会打印具有回程保证的最短十进制表示。TODO更详细地了解它与https://dev59.com/-nRB5IYBdhLWcg3wro2B#554134中显示的dbl::max_digits10
相比如何使用{:.{}}
:
#include <format>
#include <limits>
#include <string>
int main() {
std::cout << std::format("{:.{}}\n",
3.1415926535897932384626433, dbl::max_digits10);
}
另请参阅:
IEEE 754 浮点数采用基于 2 的表示方法来存储。任何基于 2 的数字都可以被表示为十进制(基于 10)的完全精度。然而,所有提出的答案都截断了小数部分。
这似乎是由于对 std::numeric_limits<T>::max_digits10
表示的误解造成的:
std::numeric_limits<T>::max_digits10
的值是必要的十进制数字的数量,以唯一地表示类型T
的所有不同的值。
换句话说:这是输出的最坏情况下所需的数字位数,以便从二进制到十进制再到二进制进行回路处理,而不会丢失任何信息。如果您输出至少 max_digits10
个小数并重建一个浮点值,则保证得到与开始时完全相同的二进制表示。
重要的是:max_digits10
通常既不能产生最短的十进制数,也不足以表示完整的精度。我不知道 C++ 标准库中是否有一个常量来编码包含浮点值的完整精度所需的最大十进制位数。我相信对于 double
类型,这个数字大约是 7671。输出带有完整精度的浮点值的一种方法是使用足够大的精度值(例如)2,并让库去掉任何尾随零:
#include <iostream>
int main() {
double d = 0.1;
std::cout.precision(767);
std::cout << "d = " << d << std::endl;
}
d = 0.1000000000000000055511151231257827021181583404541015625
max_digits10
` 所示的要多得多。
std::to_chars
以实现该转换。默认情况下,它生成保留所有信息的任何给定浮点值的最短十进制表示。#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;
}
0.1
0.10000001
0.10000002
1 这是根据Stephan T. Lavavej在CppCon 2019演讲中的内容,名为浮点数 <charconv>:使用C++17的终极武器使代码快10倍。(整个演讲值得一看。)
2 这也需要使用科学计数法(scientific
)和定点数(fixed
)的组合,以较短的方式表示。我不知道如何使用C ++标准I/O库来设置此模式。
DBL_TRUE_MIN
附近长度是否增加? - IInspectableDBL_TRUE_MIN
只在尾数中设置了最低有效位。我之前没有想到这一点。不过,我仍然需要看到一个数学证明才能理解为什么会导致最长的十进制序列。 - IInspectablelong double::max_digits10
+ 3个有效数字。我们可能无法获得完整的精度。 - chux - Reinstate Monicalong double::max_digits10
+ 3个有效数字。" - 我在C++标准文本中没有找到这个要求。这是从C语言中引用的吗?如果是,您知道我可以在哪里找到吗? - IInspectable