gpt4 book ai didi

c++ - 双印不失精度

转载 作者:IT老高 更新时间:2023-10-28 12:39:07 24 4
gpt4 key购买 nike

如何将 double 打印到流中,以便在读入时不会丢失精度?

我试过了:

std::stringstream ss;

double v = 0.1 * 0.1;
ss << std::setprecision(std::numeric_limits<T>::digits10) << v << " ";

double u;
ss >> u;
std::cout << "precision " << ((u == v) ? "retained" : "lost") << std::endl;

这并没有像我预期的那样工作。

但我可以提高精度(这让我感到惊讶,因为我认为digits10 是所需的最大值)。

ss << std::setprecision(std::numeric_limits<T>::digits10 + 2) << v << " ";
// ^^^^^^ +2

这与有效位数有关,前两位不计入(0.01)。

那么有没有人研究过精确地表示 float ?我需要在流中执行的确切魔法咒语是什么?

经过一些实验:

问题在于我的原始版本。字符串中小数点后有非有效数字影响准确性。

所以为了弥补这一点,我们可以用科学记数法来弥补:

ss << std::scientific
<< std::setprecision(std::numeric_limits<double>::digits10 + 1)
<< v;

这仍然不能解释 +1 的必要性。

另外,如果我打印出更精确的数字,我会得到更高的打印精度!

std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits) << v << "\n";

结果:

1.000000000000000e-02
1.0000000000000002e-02
1.00000000000000019428902930940239457413554200000000000e-02

基于以下@Stephen Canon 的回答:

我们可以使用 printf() 格式化程序“%a”或“%A”准确地打印出来。为了在 C++ 中实现这一点,我们需要使用固定的和科学的操纵器(参见 n3225:22.4.2.2.2p5 表 88)

std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << v;

现在我已经定义了:

template<typename T>
std::ostream& precise(std::ostream& stream)
{
std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
return stream;
}

std::ostream& preciselngd(std::ostream& stream){ return precise<long double>(stream);}
std::ostream& precisedbl(std::ostream& stream) { return precise<double>(stream);}
std::ostream& preciseflt(std::ostream& stream) { return precise<float>(stream);}

下一篇:我们如何处理 NaN/Inf?

最佳答案

说“ float 不准确”是不正确的,尽管我承认这是一个有用的简化。如果我们在现实生活中使用以 8 或 16 为基数,那么周围的人会说“以 10 为基数的十进制小数包不准确,为什么有人会编造它们?”。

问题是整数值从一个基数精确地转换为另一个基数,但小数值不会,因为它们代表积分步长的分数,并且只使用了其中的一小部分。

浮点运算在技术上是完全准确的。每个计算都有一个且只有一个可能的结果。 存在一个问题,它是大多数小数具有重复的base-2表示。事实上,在 0.01、0.02、... 0.99 的序列中,只有 3 个值具有精确的二进制表示。 (0.25、0.50 和 0.75。)有 96 个值重复,因此显然没有准确表示。

现在,有多种方法可以写入和读取 float 而不会丢失一个位。这个想法是避免尝试用以 10 为底的小数来表示二进制数。

  • 将它们写成二进制。如今,每个人都实现了 IEEE-754 格式,所以只要您选择一个字节顺序并只写入或读取该字节顺序,那么数字将是可移植的。
  • 将它们写为 64 位整数值。在这里您可以使用通常的基数 10。(因为您表示的是 64 位别名整数,而不是 52 位小数。)

您也可以只写更多的小数位数。这是否逐位准确取决于转换库的质量,我不确定我是否会在这里指望完美的准确性(来自软件)。但是任何错误都会非常小,并且您的原始数据肯定没有低位信息。 (没有一个物理和化学常数是 52 位已知的,地球上的任何距离也没有被测量到 52 位精度。)但是对于可以自动比较逐位精度的备份或恢复,这显然不理想。

关于c++ - 双印不失精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4738768/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com