C++ 舍入算法中的 0.501,C++ 中的 Excel ROUND

Posted

技术标签:

【中文标题】C++ 舍入算法中的 0.501,C++ 中的 Excel ROUND【英文标题】:0.501 in the C++ rounding algorithm, Excel ROUND in C++ 【发布时间】:2016-02-22 14:40:32 【问题描述】:

我有以下用于通常舍入的旧代码(四舍五入,类似于 Excel ROUND 函数中的舍入)。我对此代码有疑问。

    为什么要使用 0.50x 加法?我知道,应该使用 0.5 加法来实现“四舍五入”条件,但为什么会有 0.501?我们的经验表明,0.501 在实践中效果很好,它引入的舍入误差(RoundFloat 与 Excel ROUND 不同的情况)比 0.5 少得多。这是为什么?浮点计算是否偏向于给出比理想情况下更小的值,而 0.501 可以纠正这些偏差? 虽然 0.501 在大多数情况下效果很好,但某些值存在问题,例如RoundFloat(7.33499999999, 2) 给出 7.34,但这是错误的,正确答案应该是 7.33。如何改进算法以获得正确的结果?可以尝试使用 0.50000001 而不是 0.501,但总有一些参数会给出错误的结果。 什么是正确的算法,是否有内置的 C++ 函数用于 Excel 风格的 ROUND 舍入。

提前致谢!

double LongLong( double value ) 
  long long l = ( long long ) value;
  return l;


double RoundFloat( double * value, double * tonearest ) 
  double ad;
  long long mzr; 
  double resval;
  if ( ( *tonearest < 0 ) || ( *tonearest > 6 ) ) return * value;

  if ( *value < 0.000000001 )
    ad = -0.501; 
  else
    ad = 0.501;  

  mzr = LongLong(*value);   
  resval = *value - mzr;

  switch ( ( long ) *tonearest )
     
   case 0 : resval= LongLong( resval+ad);
   case 1 : resval= LongLong( resval*10+ad)/10;
   case 2 : resval= LongLong( resval*100+ad)/100;
   case 3 : resval= LongLong( resval*1000+ad)/1000;
   case 4 : resval= LongLong( resval*10000+ad)/10000;
   case 5 : resval= LongLong( resval*100000+ad)/100000;
   case 6 : resval= LongLong( resval*1000000+ad)/1000000;
   default: resval= resval;
  
   resval = resval+mzr;
   return resval;

【问题讨论】:

【参考方案1】:

(我将此答案限制为 IEEE754 浮点,这是 Excel 使用的。)

Excel 使用

0.5(舍入 pivot),因为这是正确的做法。请注意,0.5(作为二元有理数)可以用浮点数精确表示。

使用 0.501 是任意的、过度不对称的,并且表明缺乏对浮点的理解。

可以得到一些虚假效果,因为数字略小于 0.5,而 0.5 作为最接近的可表示数字。所以你的“少一点”数字会被四舍五入,即使它真的应该被四舍五入。但这更多是因为数字一开始就无法准确表示。因此,虽然 0.5 会给您带来一些奇怪的效果,但它平均而言会比 0.501 或类似值更准确。并且执行诸如添加超过 0.5 之类的操作会混淆向下舍入最佳的情况。

虽然我专注于 0.5,但所有舍入效果都可以用相同的方式解释,尽管在其他情况下舍入枢轴本身可能无法表示。

学会接受效果,就像 Excel 一样。 (请记住,Excel 可以以一种对用户隐藏这些影响的方式来格式化数据,因此它似乎在做一些比实际更特别的事情。)如果计算准确到超过 15 个显着性是绝对关键的数字,然后使用支持任意精度的类。

【讨论】:

以上是关于C++ 舍入算法中的 0.501,C++ 中的 Excel ROUND的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中的舍入值。为啥 printf、iostream 和 round 函数 [可能] 表现不同,具体取决于 Visual Studio 和 Windows 版本?

什么是 C++ 中的“<?=”运算符? [复制]

在 C++ 中使用 floor 函数的舍入错误

将C ++中的浮点数舍入到一个小数位

C++ 中的草火算法

C++中的Ciphersaber解密算法