4

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

#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 更改为浮点数,这将起作用,因为精度较低,但我需要双精度。

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

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

谢谢。

4

2 回答 2

11

这几乎是这里许多问题的完全相同 - 基本上没有二进制浮点中 34.38 的精确表示,因此您的 34 + 19/50 表示为 34 + k/n,其中 n 是 2 的幂,并且不存在以 50 为因子的 2 的精确幂,因此不存在 k 的精确值。

如果您设置输出精度,您可以看到最好的双精度表示并不精确:

cout << fixed << setprecision ( 20 );

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

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

使用浮点数时,乘法恰好是向下舍入而不是向上舍入,因此您碰巧得到了精确的结果。这不是您可以依赖的行为。

于 2010-07-08T10:57:07.860 回答
2

这里的“问题”只是 34.38 不能用双精度浮点精确表示。你应该阅读这篇文章,它描述了为什么不可能用浮点数精确表示十进制值。

如果您要检查十六进制的“34.38 * 100”(例如,按照 MATLAB 中的“十六进制格式”),您会看到:

40aadc0000000001

注意最后一个数字。

于 2010-07-08T10:57:31.027 回答