【问题标题】:How to get around some rounding errors?如何解决一些舍入错误?
【发布时间】:2011-03-07 02:44:51
【问题描述】:

我有一个在 .NET 中处理一些地理坐标的方法,并且我有一个存储坐标对的结构,这样如果为其中一个坐标传入 256,它就会变为 0。但是,在一个特定的实例中计算出大约 255.99999998 的值,并因此存储在结构中。当它在 ToString() 中打印时,它变成 256,这不应该发生 - 256 应该是 0。我不介意它是否打印 255.9999998,但是当调试器显示 255.99999998 时它打印 256 是一个问题。同时存储和显示 0 会更好。

特别是比较存在问题。 255.99999998 足够接近 256 以至于它应该等于它。比较双打时应该怎么做?使用某种 epsilon 值?


编辑:具体来说,我的问题是我取一个值,执行一些计算,然后对该数字执行相反的计算,我需要准确地取回原始值。

【问题讨论】:

  • 使用 epsilon 是比较浮点值的唯一合法方法。绝对(a - b)
  • 用 == 比较两个浮点数是完全合法的,因此几乎没有编译器抱怨它。但是,最小的舍入误差可能会导致两个值在数学上应该相等时不相等,这就是为什么如果您重视自己的理智,则不建议这样做。
  • 如果你真的只需要准确地取回原始值,你不能把它存储起来,或者一些关于原始计算过程的信息和数字一起存储吗?

标签: math floating-point double floating-accuracy epsilon


【解决方案1】:

您必须自行决定两个值相等的阈值。这相当于使用所谓的定点数(相对于浮点数)。然后,您必须手动执行向上舍入。

我会使用一些已知大小的无符号类型(例如 uint32 或 uint64,如果它们可用,我不知道 .NET)并将其视为定点数类型 mod 256。

例如。

typedef uint32 fixed;

inline fixed to_fixed(double d)
{
    return (fixed)(fmod(d, 256.) * (double)(1 << 24))
}

inline double to_double(fixed f)
{
    return (double)f / (double)(1 << 24);
}

或更精细的东西以适应舍入约定(最接近、更低、更高、奇数、偶数)。 fixed 的最高 8 位保存整数部分,低 24 位保存小数部分。绝对精度为 2^{-24}。

请注意,这些数字的加减法自然会在 256 处折回。对于乘法,您应该小心。

【讨论】:

    【解决方案2】:

    您可以使用 epsilon 方法,但 epsilon 通常是为了绕过浮点算术有损这一事实而作假。

    您可能会考虑完全避免使用二进制浮点并使用一个不错的 Rational 类。

    如果您像使用 Rational 类型那样进行无损算术,上面的计算可能注定是 256。

    有理类型可以使用 Ratio 或 Fraction 类的名称,并且写起来相当简单

    这是example。 这里是another


    编辑....

    要了解您的问题,请考虑当十进制值 0.01 转换为二进制表示时,它不能准确地存储在有限内存中。该值的十六进制表示为 0.028F5C28F5C,其中“28F5C”无限重复。因此,即使在进行任何计算之前,您只需将 0.01 存储为二进制格式,就会失去准确性。

    使用 Rational 和 Decimal 类来解决这个问题,尽管会降低性能。有理类型通过存储分子和分母来表示您的值来避免这个问题。十进制类型使用二进制编码的十进制format,除法可能有损,但可以准确存储常见的十进制值。

    出于您的目的,我仍然建议使用 Rational 类型。

    【讨论】:

      【解决方案3】:

      您可以选择格式字符串,让您可以根据需要显示尽可能多的数字。

      比较双精度值是否相等的常用方法是减去它们,看看绝对值是否小于某个预定义的 epsilon,可能是 0.000001。

      【讨论】:

      • 使用 R 格式说明符而不是默认的 G 导致它显示正确的值。
      【解决方案4】:

      这听起来像是数字的打印方式问题,而不是存储方式的问题。 double 有大约 15 个有效数字,因此它可以精确地从 256 中分辨出 255.99999998。

      【讨论】:

        猜你喜欢
        • 2013-03-21
        • 1970-01-01
        • 2014-02-19
        • 1970-01-01
        • 2023-03-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多