【问题标题】:C++ Precision: String to DoubleC++ 精度:字符串加倍
【发布时间】:2010-07-08 10:44:35
【问题描述】:

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

#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++ 中,将字符串正确转换为双精度并实际返回准确值的最佳方法是什么?

谢谢。

【问题讨论】:

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


【解决方案1】:

这几乎与这里的许多问题完全相同 - 基本上没有二进制浮点中 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

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

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

【讨论】:

【解决方案2】:

这里的“问题”很简单,34.38 不能用双精度浮点数精确表示。你应该阅读this article,它描述了为什么不能用浮点数精确表示十进制值。

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

40aadc0000000001

注意最后一个数字。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-24
    • 2014-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-10
    相关资源
    最近更新 更多