浮点数舍入误差

Float rounding off error

#include<iostream>

long myround(float f)
{
  if (f >= UINT_MAX) return f;
  return f + 0.5f;
}

int main()
{
  f = 8388609.0f;
  std:cout.precision(16);
  std::cout << myround(f) << std::endl;
}

输出:8388610.0

我正在尝试理解输出。下一个大于的浮点数 8388609.0 是 8388610 但为什么四舍五入后的值不是 8388609?

如果您将示例更改为使用 double,那么错误就会消失。问题是 floatdouble 可以存储的有效数字位数更有限。将 0.5 添加到您的值只会超出浮点数的精度限制,导致它执行一些舍入。在这种情况下,8388609.0f + 0.5f == 8388610.0f.

#include<iostream>

long myround(double f)
{
    if (f >= UINT_MAX) return f;
    return f + 0.5;
}

int main()
{
    double f = 8388609.0;
    std::cout.precision(16);
    std::cout << myround(f) << std::endl;
}

如果您继续为您的号码添加数字,它最终也会失败 double

编辑: 您可以使用 static_assert 轻松测试它。这在我的平台 static_assert(8388609.0f + 0.5f == 8388610.0f, ""); 上编译。它可能会在您的编译器上编译。

IEEE-754 定义了几种可能的舍入模式,但在实践中,几乎经常使用的是 "round to nearest, ties to even"。这也被称为"Banker's rounding",无缘无故任何人都可以辨别。

"Ties to even" 表示如果浮点计算的 to-be-rounded 结果恰好位于两个可表示数字的中间,则舍入将沿使结果的 LSB 为零的方向进行。在您的情况下,8388609.5 介于 8388609 和 8388610 之间,但只有后者的最后一位为零,因此四舍五入是向上的。如果您改为传入 8388610.0,则结果将向下舍入;如果你通过了8388611.0,它会向上舍入。