【问题标题】:How to actually avoid floating point errors when you need to use float?当您需要使用浮点数时,如何真正避免浮点错误?
【发布时间】:2013-04-21 16:38:11
【问题描述】:

我正在尝试使用一些 UI 按钮来影响 3D 模型的翻译,以将位置移动 0.1 或 -0.1。

我的模型位置是一个 3 维浮点数,因此简单地将 0.1f 添加到其中一个值会导致明显的舍入误差。虽然我可以使用 BigDecimal 之类的东西来保持精度,但我仍然必须将它从浮点数转换为最后的浮点数,这总是会导致愚蠢的数字,让我的 UI 看起来很乱。

我可以对显示的值进行美化,但舍入错误只会随着更多的编辑而变得更糟,而且它们会使我的保存文件难以阅读。

那么当我需要使用浮点数时,我该如何避免这些错误呢?

【问题讨论】:

  • 多次加减.1f不会产生很大的错误。最明显的变化是一个单一的变化,例如当期望的结果是四但在转换为整数期间产生的结果稍微少一些并被截断为三。这将通过在转换前四舍五入来解决。如果您遇到其他错误,则可能是其他错误。请显示示例数据和代码来说明您遇到的问题。
  • 最简单:增加/减少 0.125。

标签: java floating-point precision floating-accuracy floating-point-precision


【解决方案1】:

【讨论】:

  • 问题报告使用浮点数。更改为 double 将比 Kahan 求和算法提供更多的错误减少,并且更便宜(在大多数硬件上)。但是,这两种方法都无法解决通过截断将非常轻微的错误结果转换为整数而发生的错误。
【解决方案2】:

我会使用Rational 类。那里有很多 - this one 看起来应该可以工作。

Rational 被渲染为float 时,一个重要的成本是当分母被减少到gcd 时。我发布的那个让分子和分母始终处于完全减少的状态,如果你总是加或减 1/10,这应该是非常有效的。

This 实现将值归一化(即符号一致)但未归约。

您应该选择最适合您的使用的实现。

【讨论】:

    【解决方案3】:

    一个简单的解决方案是使用固定精度。即你想要的整数 10 倍或 100 倍。

    float f = 10;
    f += 0.1f;
    

    变成

    int i = 100;
    i += 1;  // use an many times as you like
    // use i / 10.0 as required.
    

    在任何情况下,我都不会使用 float,因为你得到的舍入误差比 double 更多,几乎没有任何好处(除非你有数百万个浮点值)double 给你多 8 位数的精度和合理的舍入不会看到这些错误。

    【讨论】:

      【解决方案4】:

      如果你坚持使用浮动: 避免错误的最简单方法是使用 exact 的浮点数,但是 接近所需的值,即

      round(2^n * 值) * 1/2^n.

      n 是位数,值要使用的数字(在您的情况下为 0.1)

      在你的情况下,精度越来越高:

      n = 4 => 0.125
      n = 8(字节)=> 0.9765625
      n = 16(短)=> 0.100006103516....

      长数字链是二进制转换的产物, 实数的位数少得多。

      由于浮点数是精确的,所以加法和减法将 不会引入偏移误差,但始终会 只要位数是可预测的 不长于浮点值。

      如果您担心您的显示器会受到以下因素的影响 使用这个解决方案(因为它们是奇怪的浮点数),使用 并且只存储整数(步长增加-1/1)。 内部设置的最终值为

      x = 值 * 步长。

      随着步长增加或减少 1, 精度将被保留。

      【讨论】:

      • 你在这里的意图是什么?尽管这消除了加法和减法期间的误差,但在一定范围内,它会增加总误差,因为使用的步长 (.100006…) 与所需的步长不同。这使得计算结果与预期结果不同。您期望如何获得预期的结果?
      • 因为计算的目标是否不关心它是 0.1 还是 0.100 通常并不重要...在这种特定情况下,3D 模型会增加或减少特定的量。我使用这个技巧,例如在积分间隔中,因为如果起始位置和增加间隔是精确的浮点数,则添加会更快更精确。事实上,我建议使用 0.125,因为这意味着通过 8 步,我又有一个整数值,将 1.0 分为 8 步并不是不自然的。
      • 这个答案无法解释自己。目的显然是改变步长,而不是修复最初建议的步长出现的错误,但从问题陈述中不清楚改变的步长是否可以接受。很明显,这改变了软件的行为:随着步长的改变,从 3 到 4 不再有十个步。也许这对提问者来说是可以接受的,也许不是。但答案当然应该介绍该提案并解释它正在改变观察到的行为并解决由此产生的后果。
      猜你喜欢
      • 2019-12-15
      • 2012-11-26
      • 2016-04-24
      • 2018-01-11
      • 2011-07-12
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多