istringstream对长数字进行舍入 - 我该如何防止这种情况?

时间:2012-12-04 09:31:34

标签: c++ string stringstream istringstream

给出以下代码:

istringstream i("2.11099999999999999999");

double d;
if (!(i >> d)) {d = 0;}

cout << d << endl;

输出为2.111

我希望能够使用长数,浮点数(包括浮点数),但是当我将istringstream转换为double时,我得到一个舍入数字。

我该怎样防止这种情况?如何保持给定输入的原样?

此致

5 个答案:

答案 0 :(得分:2)

在这种情况下,你无法阻止它。 double无法精确表示值2.11099999999999999999, 所代表的值均不能将2.11099999999999999999与2.111区分开来。

http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html应告诉您需要了解的内容,以及更多内容。

如果您使用其他示例,double可以表示区分舍入和未舍入值的值,则可以执行以下操作:

#include <iostream>
#include <sstream>
#include <iomanip>

int main() {
    std::istringstream iss("2.1109999");
    double d;
    iss >> d;
    std::cout << d << "\n";
    std::cout << std::setprecision(10) << d << "\n";
}

输出:

2.111
2.1109999

但是,您应该知道,d中存储的值并不完全是2.1109999

std::cout << std::setprecision(20) << d << "\n";

输出(在我的机器上,你的可能会有所不同,因为某些运行时库根本不打印到20 s.f):

2.1109998999999999292

那是因为double将值存储在二进制中,而不是十进制。所以它只能代表终止二进制分数。 2.1109999不是终止二进制分数,基本上与三分之一不是终止小数部分的原因相同。

因此,有两种方法可以保持给定输入的原样(即准确地表示该数字):

  1. 不要转换为double,请将其保留为字符串。
  2. 不要转换为double,而是查找或编写代表小数部分和/或有理数的库。例如,GMP库有mpq_tBoost.Rational

答案 1 :(得分:0)

如上所述,你无法达到那种精确度。 您可以随时增加显示的位数,如下所示:Increase precision

答案 2 :(得分:0)

在数学空间中存在无穷多个数,并且double是有限的,即它适合64位,因此仅精确地表示这些值的子集。实际上2.111也没有完全表示,因为虽然数字是用小数点打印的,但在它们下面它们确实使用二进制,这是一个尾数(52位精度)和一个指数加号位。

尾数约为52位精度,2为52的幂为4,503,599,627,370,496

正如您所看到的,这个数字在初始4之后有15位数,因此您可以获得15位小数精度数字。

对于大多数用途,这种精度足够好。对于那些需要更高精度的场合,或者需要大量数据和小变化的特殊情况,您需要采用专业技术。有一些库可以为您提供。

答案 3 :(得分:0)

您需要将数字存储在double之外的其他内容中,这是二进制表示。

浏览一下将数字存储为十进制表示的库,我知道boost有一个http://svn.boost.org/svn/boost/sandbox/big_number/libs/multiprecision/doc/html/index.html,但还有很多其他的。

如果这似乎是一个重量级的解决方案,请尝试一次读取一个字符串istringstream,如果它是一个数字,将它从char转换为int,然后存储在数组中。

未经测试的代码

string s = "2.11099999999999999999";
char num[s.size()];
for (int i = 0; i < s.size(); ++i) {
  if (isdigit(s[i])
    num[i] = s[i] - '0';
  else
    num[i] = s[i];
}

答案 4 :(得分:0)

此处有两个单独的转换:从文本到双倍(iss >> d)的转换,并且将该双重转换为文本(std::cout << d)。第一个将输入文本的最佳近似值存储到double变量中。第二个使用默认精度;它舍入值以适应该精度,并抑制尾随零。这就是你看2.111的原因。如果要查看更多数字,请使用setprecision增加输出中的位数。正如其他人所说,任何超过15位数(输入或输出)的东西都是无稽之谈。