C ++ Precision:String to Double

时间:2010-07-08 10:44:36

标签: c++ floating-point floating-accuracy

在对转换后的字符串执行某些操作后,我遇到了double的精度问题。

#include <iostream>   
#include <sstream>
#include <math.h>

using namespace std;

// conversion function
void convert(const char * a, const int i, double &out)
{

   double val;

   istringstream in(a);
   in >> val;

   cout << "char a -- " << a << endl;
   cout << "val ----- " << val << endl;

   val *= i;

   cout << "modified val --- " << val << endl;
   cout << "FMOD ----- " << fmod(val, 1) << endl;

   out = val;

   return 0;

}

对于作为字符串输入的所有数字,情况并非如此,因此错误不是常数。 它只影响一些数字(34.38似乎是不变的)。

当我传入a = 34.38且i = 100:

时,它会返回此值
char a -- 34.38
Val ----- 34.38
modified val --- 3438
FMOD ----- 4.54747e-13

如果我将Val更改为float,这将有效,因为精度较低,但我需要一个double。

当我使用atof,sscanf和strtod而不是sstream时,这也是重复。

在C ++中,将字符串正确转换为double并实际返回准确值的最佳方法是什么?

感谢。

2 个答案:

答案 0 :(得分:11)

这几乎是这里很多问题的完全重复 - 基本上没有二进制浮点的34.38的精确表示,所以你的34 + 19/50表示为34 + k / n,其中n是幂的幂两个,并没有两个具有50作为因子的精确幂,因此没有确切的k值可能。

如果设置输出精度,则可以看到最佳双精度表示不精确:

cout << fixed << setprecision ( 20 );

给出

char a -- 34.38
val ----- 34.38000000000000255795
modified val --- 3438.00000000000045474735
FMOD ----- 0.00000000000045474735

因此,在回答您的问题时,您已经在使用将字符串转换为double的最佳方法(尽管boost lexical cast将您的两行或三行包装成一行,因此可以节省您编写自己的函数)。结果是由于双精度使用的表示,并且适用于基于二进制浮点的任何有限表示。

对于浮点数,乘法恰好是向下舍入而不是向上,所以你碰巧得到了一个确切的结果。这不是您可以依赖的行为。

答案 1 :(得分:2)

这里的“问题”只是34.38不能用双精度浮点精确表示。您应该阅读this article,其中描述了为什么不可能完全以浮点表示十进制值。

如果你用十六进制检查“34.38 * 100”(例如在MATLAB中的“格式十六进制”),你会看到:

40aadc0000000001

注意最后的数字。