在我之前的问题中,我打印了一个double using cout,当我没有预料到它时,它变得圆润了。我如何才能使cout打印一个双重使用全精度?
当前回答
我如何打印一个双值与全精度使用cout?
使用六漂或 使用科学,设定精度
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
否则:是固定的还是科学的?
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的双精度编码在2的各种幂之间编码相同的精度。通常是53位。
[1.0…2.0)有253种不同的双, [2.0…4.0)有253种不同的双, [4.0…8.0)有253种不同的双, [8.0…10.0)有2/8 * 253个不同的双。
然而,如果代码以N位有效数字的十进制方式打印,则组合的数量[1.0…10.0)为9/10 * 10N。
无论选择什么N(精度),在双精度文本和十进制文本之间都不会有一对一的映射。如果选择一个固定的N,有时它会略多于或少于某些双精度值的实际需要。我们可能会因为太少(下面a)或太多(下面b)而出错。
3候选人N:
a)使用N,当从文本-双-文本转换时,我们会得到相同的文本。
std::cout << dbl::digits10 << '\n';
// Typical output
15
b)使用N,因此当从double-text-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-ish)时,这可能很有用。然而,由于这是“工作”,不太可能是OP的目标,它将被搁置一边。
通常是b)用于“完全精确地打印双精度值”。有些应用程序可能倾向于a)错误而不提供太多信息。
使用.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
相似的C题
其他回答
在这个问题中,有一个关于如何无损地将double类型转换为字符串的描述(在Octave中,但它可以很容易地在c++中重现)。我们的想法是对浮点数有一个人类可读的简短描述和一个十六进制形式的无损描述,例如:pi -> 3.14{54442d18400921fb}。
printf("%.12f", M_PI);
%。12f表示浮点数,精度为12位。
IEEE 754浮点值使用2进制表示存储。任何以2为底的数字都可以表示为精确的十进制(以10为底)。然而,提出的答案中没有一个是这样的。它们都截断了十进制值。
这似乎是由于曲解std::numeric_limits<T>::max_digits10表示:
std::numeric_limits<T>::max_digits10的值是唯一表示T类型的所有不同值所必需的以10为基数的数字的数量。
换句话说:如果您希望在不丢失任何信息的情况下,从二进制到十进制再到二进制进行往返,则输出所需的位数(最坏情况)。如果输出至少max_digits10个小数并重新构造一个浮点值,则可以保证得到与开始时完全相同的二进制表示。
What's important: max_digits10 in general neither yields the shortest decimal, nor is it sufficient to represent the full precision. I'm not aware of a constant in the C++ Standard Library that encodes the maximum number of decimal digits required to contain the full precision of a floating point value. I believe it's something like 767 for doubles1. One way to output a floating point value with full precision would be to use a sufficiently large value for the precision, like so2, and have the library strip any trailing zeros:
#include <iostream>
int main() {
double d = 0.1;
std::cout.precision(767);
std::cout << "d = " << d << std::endl;
}
这将产生以下输出,其中包含完整的精度:
d = 0.1000000000000000055511151231257827021181583404541015625
注意,这比max_digits10建议的数字要多得多。
While that answers the question that was asked, a far more common goal would be to get the shortest decimal representation of any given floating point value, that retains all information. Again, I'm not aware of any way to instruct the Standard I/O library to output that value. Starting with C++17 the possibility to do that conversion has finally arrived in C++ in the form of std::to_chars. By default, it produces the shortest decimal representation of any given floating point value that retains the entire information.
它的接口有点笨拙,你可能想把它包装成一个函数模板,返回一些你可以输出到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;
}
这将打印出(使用微软的c++标准库):
0.1
0.10000001
0.10000002
1摘自Stephan T. Lavavej的CppCon 2019演讲,题为《浮点<charconv>:用c++ 17的Final Boss让你的代码快10倍》。(整个演讲都值得一看。)
2这也需要使用scientific和fixed的组合,以较短者为准。我不知道如何使用c++标准I/O库设置此模式。
通过完全精度,我假设有足够的精度来显示与预期值的最佳近似值,但应该指出的是,double是使用以2为基数的表示来存储的,以2为基数不能准确地表示像1.1这样微不足道的东西。获得实际双精度(没有舍入错误)的唯一方法是打印出二进制位(或十六进制位)。
一种方法是使用并集将双精度值输入为整数,然后输出整数,因为整数不会受到截断或舍入问题的影响。(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;
最轻松的……
#include <limits>
using std::numeric_limits;
...
cout.precision(numeric_limits<double>::digits10 + 1);
cout << d;