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

时间:2009-02-16 18:15:18

标签: c++ floating-point precision iostream cout

所以我得到了last question的答案(我不知道为什么我没想到)。我正在使用double打印cout,当我没想到它时,它会变得圆润。如何使用全精度cout打印double

17 个答案:

答案 0 :(得分:346)

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

答案 1 :(得分:58)

使用std::setprecision

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;

答案 2 :(得分:23)

以下是我要使用的内容:

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

基本上,限制包具有所有类型构建的特征 浮点数(float / double / long double)的特征之一是digits10属性。这定义了基数10中浮点数的准确性(我忘记了确切的术语)。

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

答案 3 :(得分:13)

iostreams的方式有点笨重。我更喜欢使用boost::lexical_cast,因为它为我计算了正确的精度。还有it's fast

#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

答案 4 :(得分:10)

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

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;

显示:

  

100.0000000000005


max_digits10是唯一表示所有不同double值所必需的位数。 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>

答案 5 :(得分:9)

通过完全精确,我假设足够的精度来显示预期值的最佳近似值,但应该指出double使用基数2表示存储而基数2不能表示为琐碎的东西确切地说是1.1。获得实际双精度的全满精度(NO ROUND OFF ERROR)的唯一方法是打印出二进制位(或十六进制数)。一种方法是将double写入union,然后打印出位的整数值。

union {
    double d;
    uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;

这将为您提供100%准确的双精度...并且完全不可读,因为人类无法读取IEEE双格式! Wikipedia对如何解释二进制位有很好的解释。

在较新的C ++中,你可以做到

std::cout << std::hexfloat << 1.1;

答案 6 :(得分:7)

在C ++ 20中,您将可以使用std::format来做到这一点:

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

输出(假设IEEE754 double):

3.141592653589793

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

在此期间,您可以使用the {fmt} librarystd::format是基于。 {fmt}还提供了print功能,使此操作变得更加轻松和高效(godbolt):

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

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

答案 7 :(得分:4)

  

如何使用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)精度之一。 precision 的答案过多,无法提供所需的正确值。因此,这个答案是一个古老的问题。

  1. 什么基础?

肯定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

  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 ... 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和十进制文本之间都不会存在一对一的映射。如果是固定的{{1}选择},有时它会比某些N值实际需要多一些或少一些。我们可能会因太少(下面的double)或太多(下面的a))而出错。

3个候选人b)

a)使用N,因此从文本N-文本转换时,所有double的文本都相同。

double

b)使用std::cout << dbl::digits10 << '\n'; // Typical output 15 ,因此从N-text-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来确保打印足够的十进制数字。

c)使用随值变化的digits10 + 3

当代码希望显示最小文本(N)或N == 1double exact 值(在{ {1}})。然而,由于这是“工作”,而不是OP的目标,因此将其搁置一旁。


通常是b)用于“以全精度打印N == 1000-ish值”。某些应用程序可能更喜欢a)由于没有提供太多信息而出错。

使用denorm_mindouble设置小数点后要打印的位数,因此将打印.scientific位。代码总共需要.precision()位数字,因此1 + .precision()max_digits10来调用。

.precision()

Similar C question

答案 8 :(得分:3)

printf("%.12f", M_PI);

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

答案 9 :(得分:3)

IEEE 754浮点值使用2表示形式存储。任何以2为底的数字都可以以十进制(以10为底)表示为全精度。但是,建议的答案都没有。他们全部截断了十进制值。

这似乎是由于误解了std::numeric_limits<T>::max_digits10所代表的含义:

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

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

重要的是:max_digits10通常既不产生最短的小数,也不足以表示完整的精度。我不知道C ++标准库中的常量,该常量编码包含浮点值的全精度所需的最大十进制数字。我相信double ss 1 大约是767。输出全精度浮点值的一种方法是使用足够大的精度值like so 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;
}

这将打印出来(使用Microsoft's C++ Standard Library):

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库设置此模式的方法。

答案 10 :(得分:1)

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

答案 11 :(得分:1)

C ++ 20 std::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.145

https://stackoverflow.com/a/65329803/895245所述,如果您未明确通过精度,它将打印带有往返保证的最短十进制表示形式。 TODO更详细地了解了它与dbl::max_digits10{:.{}}的比较:#include <format> #include <limits> #include <string> int main() { std::cout << std::format("{:.{}}\n", 3.1415926535897932384626433, dbl::max_digits10); }

&

另请参阅:

答案 12 :(得分:1)

cout是一个对象,它有一堆方法可以调用来改变打印内容的精度和格式。

有一个setprecision(...)操作,但你也可以设置其他东西,如打印宽度等。

在IDE的参考中查找cout。

答案 13 :(得分:0)

这是一个适用于任何浮点类型的函数,不仅适用于double,还可以将流放回其后的查找位置。不幸的是,它无法与线程很好地交互,但这就是iostream的本质。您需要在文件开头包含以下内容:

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

答案 14 :(得分:0)

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

#include <iostream>
#include <iomanip>

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

查看此处: Fixed-point notation

std::fixed

使用固定的浮点符号为以下内容设置浮点格式格式标志 将str流修复。

将floatfield设置为固定值时,将写入浮点值 使用定点表示法:值用精确表示为 精度字段指定的小数部分中的许多数字 (精确)并且没有指数部分。

std::setprecision

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

如果您熟悉用于表示浮点的IEEE标准,那么您将知道不可能以超出标准范围的精确度显示浮点,也就是说,它总是会导致实际值四舍五入。

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

cout << defaultfloat << d ;

std::defaultfloat

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

当floatfield设置为defaultfloat时,浮点值为 使用默认符号编写:该表示使用了尽可能多的 所需的有效数字,直至流的小数精度 (精度),同时计算小数点前后的位数 点(如果有)。

这也是cout的默认行为,这意味着您没有明确使用它。

答案 15 :(得分:0)

使用ostream :: precision(int)

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

将产生

3.141592653589793, 2.718281828459045

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

答案 16 :(得分:0)

最便携......

#include <limits>

using std::numeric_limits;

    ...
    cout.precision(numeric_limits<double>::digits10 + 1);
    cout << d;