浮点到双精度转换的舍入误差

float rounding error float to double conversion

本文关键字:舍入误差 转换 双精度      更新时间:2023-10-16

我有一个系统,我现在不能改变,是使用浮动存储信息。

我有一个舍入误差的问题。例子:

std::string floatToStr(float d)
{
    std::stringstream ss;
    ss << std::fixed << std::setprecision(15) << d;
    return ss.str();
}
    float val723 = 0.575f;
    std::cout << floatToStr(val723) << std::endl;

结果

0.574999988079071

我可以用字符串处理来纠正这个问题。

std::string doubleToStr(double d, int precision)
{
    std::stringstream ss;
    ss << std::fixed << std::setprecision(precision) << d;
    return ss.str();
}
double val945 = (double)0.575f;
std::cout << doubleToStr(atof(doubleToStr(val945, 4).c_str()), 15) << std::endl;

结果是:

0.575000000000000

但这是一个昂贵的解决方案。有没有更好的解决方案可以在实时过程中使用?(实际上我不需要在我的实时代码中使用它,但我准备好了,如果我必须在实时代码中使用它。)

Edit1:我知道我可以使用小数点后的6或7位数字浮点类型。

您可以用代码打破。PC的工作方式是二进制,而0.575是一个必须近似的小数。

正确的解决方案是将浮点数打印为不超过其物理存储的数字(7),并打印其真实值,而不是您认为它应该包含的十进制值。

你要的是小数点后15位,这就是它给你的。抱怨这个似乎有点矛盾!

该值是浮点数,这意味着6个十进制数字是可用精度的极限(如果您确信几乎没有舍入误差,则为7)。要求15位小数,后面的。'显示浮点数的ls位的状态,它会受到舍入和表示错误的影响。

您似乎想要做的是将浮点数的值呈现为双精度,即小数的15位,然后您需要将浮点数的双精度版本四舍五入到6(或者,如果您觉得勇敢的话)7 小数位的精度。这与二进制到十进制的转换非常相似,但您需要准备值的双副本,以便输出。

这与将值的字符串版本转换为6位数并将其转换为双精度值相差不远。你可以为自己建立更快!

不幸的是,仍然存在一个问题。您要求在 '.'后面输入15位小数。这将显示大于10.0的值以及介于1.0和10.00之间的值的表示错误。例如:如果你的浮点值是(比如说)57.51234f(其实际值大约是57.512340545654297);下面的代码将提供575124.0/10000.0作为双精度数,当输出时得到:57.512300000000003——因为您要求的是比双精度数多出大约1位的数字。(对于5751.234f,同样的过程给出5751.229999999999563.)

我会仔细考虑为什么'后面有15个小数。'是必需的,特别是当数据只有6-7位总精度时——所以在大多数中,'之后的6-7位"好"数字。',取决于数字的大小。

顺带一提:你可以把数字转换成科学形式,然后直接处理字符串——这是下面代码所做的另一种方式。


  double pt[] = { 1E0,  1E1,  1E2,  1E3,  1E4,  1E5,  1E6,  1E7,  1E8,  1E9,
                  1E10, 1E11, 1E12, 1E13, 1E14, 1E15, 1E16, 1E17, 1E18, 1E19 } ;
  double xx ;
  int de ;
  de = 6 - (int)ceilf(log10f(x)) ;   /* where 'x' is the float to be shown */
  if ((de > 15) || (de < -18))       /* de > 15 -- no need to round, value too small  */
                                     /* de < 18 -- cannot round, value too big        */
    xx = x ;         /* xx is value to output */
  else
    {
      while (1)
        {
           xx = x ;
           if      (de < 0)
             xx /= pt[-de] ;
           else if (de > 0)
             xx *= pt[+de] ;
           xx = round(xx) ;
           if      (xx < pt[5])
             de += 1 ;
           else if (xx > pt[6])
             de -= 1 ;
           else
             break ;
        } ;
      if      (de < 0)
        xx *= pt[-de] ;
      else if (de > 0)
        xx /= pt[+de] ;
    } ;
  ss << std::fixed << std::setprecision(15) << xx ;