在对转换后的字符串执行某些操作后,我遇到了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并实际返回准确值的最佳方法是什么?
感谢。
答案 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
注意最后的数字。