【问题标题】:Float rounding off error浮点数舍入误差
【发布时间】:2017-02-16 20:37:01
【问题描述】:
#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?

【问题讨论】:

  • “下一个大于 8388609.0 的浮点数是 8388610” 你是怎么得出这个结论的?
  • 单精度浮点数没有足够的空间来保证所有这些十进制数字(超过六个)的表示。你想做什么?
  • 此代码无法编译。您没有f 的声明。发布您的实际 minimal reproducible example 导致所述输出。
  • @LightnessRacesinOrbit 2^24 以下的所有整数都可以精确表示为单精度浮点数,2^23 以上的所有单精度浮点数都是整数。
  • @LightnessRacesinOrbit ...是吗?不知道你在说什么。 8388609 之后的下一个 float 是 8388610 的事实直接来自这些属性,所有七位整数都可以表示为 floats 的事实也是如此。

标签: c++ floating-point precision


【解决方案1】:

IEEE-754 定义了几种可能的舍入模式,但在实践中,几乎总是使用的一种是“舍入到最接近,结到偶数”。这也被称为“银行家四舍五入”,任何人都无法辨别。

“与偶数相关”表示如果浮点计算的待舍入结果恰好在两个可表示数字之间的中间,则舍入将在使结果的 LSB 为零的任何方向上进行。在您的情况下,8388609.5 介于 8388609 和 8388610 之间,但只有后者的最后一位为零,因此向上舍入。如果您改为通过 8388610.0,则结果将向下舍入;如果你在 8388611.0 中通过了,它会向上四舍五入。

【讨论】:

  • 感谢您的解释。回答我的问题。另外,我假设浮点硬件决定了如何进行舍入。这是否意味着 FP 硬件实际上计算了精确值并根据舍入策略映射到可表示的浮点表示?
  • 这回答了我关于 FP 硬件的问题。 pages.cs.wisc.edu/~markhill/cs354/Fall2008/notes/…
【解决方案2】:

如果您将示例更改为使用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, ""); 上编译。它可能会在你的上编译。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-01
    • 2020-08-04
    • 1970-01-01
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 2019-01-21
    相关资源
    最近更新 更多