我如何打印一个双值与全精度使用cout?

在我的早些时候的问题中,我使用cout打印了一个double,当我没想到它会四舍五入。如何使cout使用全精度打印double ?

627666 次浏览

使用std::setprecision:

#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;

可以直接在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;

最轻松的……

#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;
基本上,限制包具有所有内置类型的特征 浮点数(float/double/long double)的特征之一是digits10属性。这定义了以10为基数的浮点数的精度(我忘记了确切的术语) < p >看到:http://www.cplusplus.com/reference/std/limits/numeric_limits.html < br >

. properties

. properties

上ostream::精密(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;

输出:

Pi: 3.14159265358979

所谓完全精度,我认为是指有足够的精度来显示与预期值的最佳近似值,但应该指出的是,double是使用以2为基数的表示法存储的,而以2为基数的表示法不能准确地表示像1.1这样微不足道的东西。获得实际双精度的饱饱精度(没有舍入错误)的唯一方法是打印出二进制位(或十六进制位)。

一种方法是使用uniondouble输入为整数,然后打印整数,因为整数不会受到截断或舍入问题的影响。(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

< p > < br > Max_digits10是唯一表示所有不同双精度值所需的位数。Max_digits10表示小数点前后的位数 < p > < br > 不要在std::fixed中使用set_precision(max_digits10)。 < br > 在固定表示法中,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>

如何使用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)精确的。使用精度的太多答案不能提供所需的正确值。这就是对一个老问题的回答。

  1. 基础是什么?

a__abc0肯定是使用进制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

  1. 否则:fixedscientific?

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

  1. 有多少精度(总共有多少位数字)?

使用二进制基数2编码的double在2的各种幂之间编码相同的精度。通常是53位。

(1.0 < p > 2.0…)有两个不同double __abc1,
[2.0…4.0)有2个__abc1不同的double
[4.0…8.0)有2个__abc1不同的double
[8.0…10.0)有2/8 * 253不同的double.

然而,如果代码打印为具有N有效数字的十进制,则组合的数量[1.0…10.0)为9/10 * 10N

无论选择__ABC0(精度),double和十进制文本之间都不会有一对一的映射。如果选择了一个固定的N,有时它会略多于或少于某些double值的实际需要。我们可能会在太少(下面的a))或太多(下面的b))上出错。

3 candidate N:

a)使用N,这样当从text-double-text转换时,所有double都得到相同的文本。

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)用于“完全精确地打印double值”。有些应用程序可能倾向于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问题

这将显示点后小数点后两位的值。

#include <iostream>
#include <iomanip>


double d = 2.0;
int n = 2;
cout << fixed << setprecision(n) << d;

在这里看到的: 定点表示法

std::fixed

使用固定浮点符号设置floatfield格式标志

当floatfield设置为fixed时,写入浮点值 使用定点表示法:值完全用as表示 由精度字段指定的小数部分中的多个数字 (精度)并且没有指数部分。

std::setprecision

Set decimal precision设置格式化使用的十进制精度 输出操作的浮点值

如果您熟悉表示浮点数的IEEE标准,就会知道在标准范围之外完全精确地显示浮点数是不可能的,也就是说,它总是会导致实际值的舍入。

您需要首先检查该值是否在范围内,如果是,那么使用:

cout << defaultfloat << d ;

std::defaultfloat

使用默认浮点符号设置floatfield格式标志

. STR流为defaultfloat

当floatfield设置为defaultfloat时,浮点值为 使用默认表示法编写:表示使用尽可能多的表示法 需要的有意义的数字,直到流的十进制精度 (精度),同时计算小数点前后的数字 点(如果有).

这也是cout的默认行为,这意味着你不显式地使用它。

这里是一个适用于任何浮点类型的函数,而不仅仅是double,并且还将流放回之后找到它的方式。不幸的是,它不能很好地与线程交互,但这是iostreams的本质。你需要在文件的开头包含以下内容:

#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所代表的含义的误解:

std::numeric_limits<T>::max_digits10的值是以10为基数的数字的数量,这些数字是唯一表示T类型的所有不同值所必需的。

换句话说:如果您希望在不丢失任何信息的情况下,从二进制到十进制再到二进制进行往返,则输出所需的位数(最坏情况)。如果输出至少max_digits10小数并重构浮点值,则保证得到与开始时完全相同的二进制表示。

重要的是:max_digits10通常不会产生最短的小数,也不足以表示完整的精度。我不知道c++标准库中有一个常量编码了包含浮点值的完整精度所需的最大十进制位数。我相信它是类似767的doubles1。输出完全精度的浮点值的一种方法是使用一个足够大的精度值像这样2,并让标准库去掉任何尾随零:

#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;
}

这将打印出(使用微软的c++标准库):

0.1
0.10000001
0.10000002

1 摘自Stephan T. Lavavej在2019年CppCon大会上题为 float - point <charconv>:用c++ 17的Final Boss让你的代码速度快10倍。(整个演讲都值得一看。)

2 这也需要使用__ABC0和fixed的组合,以较短者为准。我不知道如何使用c++标准I/O库设置此模式。

20 std::format c++

这个伟大的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://stackoverflow.com/a/65329803/895245中提到的,如果不显式传递精度,它会打印具有往返保证的最短十进制表示。TODO更详细地了解它如何与:dbl::max_digits10进行比较,如https://stackoverflow.com/a/554134/895245{:.{}}所示:

#include <format>
#include <limits>
#include <string>


int main() {
std::cout << std::format("{:.{}}\n",
3.1415926535897932384626433, dbl::max_digits10);
}

参见:

在c++ 20中,你将能够使用std::format来做到这一点:

std::cout << std::format("{}", M_PI);

输出(假设IEEE754 double):

3.141592653589793

默认浮点格式是具有往返保证的最短十进制表示形式。与setprecision I/O操纵符相比,此方法的优点是它不会打印不必要的数字。

在此期间,你可以使用{fmt}库std::format是基于。{fmt}还提供了print函数,使这更容易和更有效(godbolt):

fmt::print("{}", M_PI);

免责声明:我是{fmt}和c++ 20 std::format的作者。

这个问题中有一个关于如何无损地将double类型转换为字符串的描述(在Octave中,但它可以很容易地在c++中重现)。我们的想法是有一个简短的、人类可读的浮点数描述和一个六边形的无损描述,例如:pi ->3.14 {54442 d18400921fb}。

最好的选择是使用std::setprecision,解决方案是这样的:

# include <iostream>
# include <iomanip>


int main()
{
double a = 34.34322;
std::cout<<std::fixed<<a<<std::setprecision(0)<<std::endl;
return 0;
}

注意:你不需要使用cout.setprecision来做它,我在std::setprecision处填充0,因为它必须有一个参数。