404

在我之前的问题中,我正在打印一个在我没有预料到的情况下被四舍五入的double使用。cout如何使用全精度进行cout打印?double

4

16 回答 16

469

您可以直接设置精度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;
于 2009-02-16T18:38:50.290 回答
83

使用std::setprecision

#include <iomanip>
std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
于 2009-02-16T18:18:08.513 回答
27

这是我会使用的:

std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
          << 3.14159265358979
          << std::endl;

基本上,limits 包具有所有内置类型的特征。
浮点数(float/double/long double)的特征之一是digits10属性。这定义了以 10 为底的浮点数的准确性(我忘记了确切的术语)。

有关其他属性的详细信息,请参见:http ://www.cplusplus.com/reference/std/limits/numeric_limits.html。

于 2009-02-16T22:10:04.150 回答
15

在 C++20 中,您将能够使用它std::format来执行此操作:

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

输出(假设 IEEE754 double):

3.141592653589793

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

同时你可以使用基于的 {fmt}std::format。{fmt} 还提供了print使这更容易和更高效的功能(godbolt):

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

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

于 2020-12-16T19:19:17.153 回答
14

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

于 2011-08-05T17:35:07.943 回答
13

如何double使用 cout 以全精度打印值?

使用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. 什么基础?

Adouble肯定是使用 base 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

Adouble浮点类型,而不是定点

请勿使用,因为它无法std::fixed打印小double,但0.000...000. 对于 large 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. 多少精度(总位数)?

double使用二进制基数 2 编码的A在 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之间都不会存在一对一的映射。double 如果选择了一个固定值,有时它会比某些值N真正需要的稍多或少一些。double我们可能会在太少(a)下)或太多(b)下)上出错。

3名候选人N

a) 在从 text--text 转换时使用Nso,double我们会为 all 得到相同的文本double

std::cout << dbl::digits10 << '\n';
// Typical output
15

b)N在从double-text-转换时使用 so,我们对 alldouble得出相同的结果。doubledouble

// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17

max_digits10不可用时,请注意,由于 base 2 和 base 10 属性digits10 + 2 <= max_digits10 <= digits10 + 3,我们可以使用digits10 + 3来确保打印足够的十进制数字。

c) 使用N随值变化的 an。

当代码想要显示最少的文本 ( N == 1) 或 a 的确切doubleN == 1000-ish在 的情况下denorm_min)时,这可能很有用。然而,由于这是“工作”并且不太可能是 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 题

于 2018-09-12T16:24:22.113 回答
11

以下是如何以全精度显示双精度:

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>
于 2016-01-01T13:57:17.447 回答
10

通过全精度,我假设有足够的精度来显示预期值的最佳近似值,但应该指出的double是,使用 base 2 表示存储,base 2 不能1.1准确地表示微不足道的东西。获得实际双精度的唯一方法(没有 ROUND OFF 错误)是打印出二进制位(或十六进制 nybbles)。

这样做的一种方法是使用 aunion将其键入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;
于 2015-10-24T20:36:30.767 回答
6

C++20std::format

这个伟大的新 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 更详细地了解它与以下内容的比较:如dbl::max_digits10https://stackoverflow.com/a/554134/895245所示{:.{}}

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

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

也可以看看:

于 2020-12-12T15:20:48.580 回答
4

IEEE 754浮点值使用 base 2 表示存储。任何以 2 为底的数字都可以表示为完全精度的小数(以 10 为底)。然而,所有提议的答案都没有。它们截断十进制值。

这似乎是由于对std::numeric_limits<T>::max_digits10代表的误解:

的值std::numeric_limits<T>::max_digits10是唯一表示 type 的所有不同值所必需的以 10 为基数的位数T

换句话说:如果你想从二进制到十进制再到二进制,它是输出所需的(最坏情况)位数,而不会丢失任何信息。如果您至少输出max_digits10小数并重建浮点值,则可以保证获得与开始时完全相同的二进制表示。

重要的是:max_digits10通常既不会产生最短的小数,也不足以表示完整的精度。我不知道 C++ 标准库中有一个常量,它编码包含浮点值的完整精度所需的最大十进制数字。我相信这类似于doubles 1的 767 。以全精度输出浮点值的一种方法是使用足够大的精度值,例如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. 默认情况下,它会生成保留全部信息的任何给定浮点值的最短十进制表示。

它的界面有点笨拙,您可能希望将其包装到一个函数模板中,该模板返回您可以输出的std::cout内容(如 a 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 演讲,标题为Floating-Point <charconv>: Making Your Code 10x Faster With C++17's Final Boss。(整个演讲值得一看。)

2 这还需要使用 和 的组合scientificfixed以较短者为准。我不知道使用 C++ 标准 I/O 库设置此模式的方法。

于 2020-06-23T19:59:50.913 回答
3
printf("%.12f", M_PI);

%.12f 表示浮点数,精度为 12 位。

于 2010-01-24T17:34:56.330 回答
1

最便携...

#include <limits>

using std::numeric_limits;

    ...
    cout.precision(numeric_limits<double>::digits10 + 1);
    cout << d;
于 2009-02-16T18:39:43.830 回答
1

这个问题中,有一个关于如何无损地将双精度转换为字符串的描述(在 Octave 中,但它可以在 C++ 中轻松复制)。想法是对浮点数进行简短的人类可读描述和六进制形式的无损描述,例如:pi -> 3.14{54442d18400921fb}。

于 2021-04-26T12:01:09.890 回答
0

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

#include <iostream>
#include <iomanip>

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

见这里: 定点符号

标准::固定

使用固定浮点表示法 将 str 流的浮点字段格式标志设置为固定。

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

std::setprecision

设置小数精度 设置用于格式化输出操作的浮点值的小数精度。

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

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

cout << defaultfloat << d ;

std::defaultfloat

使用默认浮点表示法 将 str 流的 floatfield 格式标志设置为 defaultfloat。

当 floatfield 设置为 defaultfloat 时,浮点值使用默认表示法写入:表示使用尽可能多的有意义的数字,直到流的小数精度(precision),计算小数点之前和之后的数字(如果有)。

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

于 2020-04-11T13:39:33.107 回答
0

这是一个适用于任何浮点类型的函数,而不仅仅是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';
于 2020-06-23T16:11:07.970 回答
-1

使用 ostream::precision(int)

cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;

将产生

3.141592653589793, 2.718281828459045

为什么你必须说“+1”我不知道,但你得到的额外数字是正确的。

于 2009-08-11T11:28:59.150 回答