【问题标题】:How to calculate floating-point precision after round-off errors in +, -, *, and /?如何在 +、-、* 和 / 中的舍入误差后计算浮点精度?
【发布时间】:2013-10-13 04:00:46
【问题描述】:

出于验证的目的,我希望能够计算出在某些特定算术计算期间由于舍入到可表示值而导致的累积误差的合理严格上限。

假设我们有一个函数foo(),它声称要执行一些特定的算术计算。还假设有一个关于最大误差(由于舍入)的隐含保证,该保证源于所涉及的类型是 floatdouble 以及 foo() 执行计算的隐含(或声明)方式。

我希望能够验证来自foo() 的特定输入值集的结果,方法是还以跟踪累积的最坏情况错误的方式执行计算,然后检查两个结果是否与最终的最坏情况错误要求一样接近。

我想可以通过引入一个新的算术类 track_prec<T> 来做到这一点,该类将精度跟踪添加到基本浮点类型之一,然后让它取决于算术运算符的实现该类来计算每个子表达式的最坏情况错误。我的问题是我不知道如何在这些一般情况下计算这些最坏情况的错误:

// T = float or double
template<class T> class track_prec {
public:
    T value;
    T ulp; // http://en.wikipedia.org/wiki/Unit_in_the_last_place

    track_prec& operator+=(const track_prec& v)
    {
        value += v.value;
        ulp = ???; // How to do this? And what about -=, *=, and /=?
    }

    friend bool operator==(T, const track_prec&)
    {
        // Exactly how should this comparison be done?
    }
};

例如,假设foo() 是对一系列数字的简单求和。那么我们可以使用track_prec&lt;T&gt;如下:

std::vector<T> values { 0.4, -1.78, 1.3E4, -9.29E3, ... };
CHECK_EQUAL(std::accumulate(values.begin(), values.end(), track_prec<T>()),
            foo(values.begin(), values.end()));

当然,任何形式的帮助都是受欢迎的,但是指向免费和工作代码的指针会非常好。

我找到了有关该主题的这些链接,但它们似乎无法直接回答我的问题。

【问题讨论】:

  • 区间算术很有吸引力,但它并没有大受欢迎。它几乎没有硬件支持。大多数机器要么不支持必要的舍入模式,要么为切换舍入模式强加高执行时间成本。大多数情况下,当需要误差范围时,它们会由人工针对特定情况进行计算或通过各种方式进行估计。
  • 好吧,不过,就我而言,性能不是问题,因为只有验证码会进行精确跟踪。但是你知道任何可以自动计算错误界限的 C/C++ 实现吗?

标签: c++ floating-point verification floating-point-precision


【解决方案1】:

在此处查看错误跟踪是如何实现的。 https://github.com/Esri/geometry-api-java/blob/master/src/main/java/com/esri/core/geometry/ECoordinate.java

基本思想是浮点运算结果将接近正确值 +- 0.5 * DBL_EPSILON * 值。所以你可以跟踪和积累它。 上面链接中的代码将计算 a + b 的绝对误差为

err(a+b) = err(a) + err(b) + DBL_EPSILON * abs(a + b). 

假设:IEEE 754 双精度浮点运算使用保护位。

【讨论】:

  • 这个答案混合了绝对误差和相对误差。舍入误差不同于 x +/- d 表达式,人们可能会使用它来表征具有绝对不确定性的测量值。相反,机器 epsilon 表征相对误差,因为舍入会影响尾数。描述舍入误差的适当表达式是 rounded(x opy) = (x opy)*(1 + d) with |d|
  • @Praxeolitic 在我的回答中,ulp 是对绝对误差的估计。
  • ulp(x op y) 可以用作运算舍入误差的上限,但这里的方程没有多大意义,尤其是使用 epsilon。
  • @Praxeolitic 你能具体说明为什么吗?此外,绝对误差相加,运算的绝对误差为 eps * abs(result)。所以总数考虑到了这一点。
  • 为了保持累积绝对舍入误差的运行上限,对于“a + b”,我们可以添加先前累积的 a 误差、先前累积的 b 误差和新引入的舍入误差.对于新的舍入误差,使用结果的 ulp 是有意义的。对于之前在 a 和 b 中累积的错误,我们需要一直在跟踪它们。无法仅根据值(例如 ulp(a))计算这些数字中的累积误差,因为舍入误差是由运算产生的,它不是浮点值所固有的。
【解决方案2】:

跟踪浮点计算精度的最简单方法称为interval arithmetic。它不需要 IEEE 754 算术,只是计算可以向上或向下舍入,以便每一步计算间隔的边界包含所有可能由相同计算产生的实数,如果它们是用实数完成的算术。

您应该能够找到许多现有的区间算术实现。

任何给定步骤的计算精度是在该步骤计算的间隔宽度。

注意常数:如果您希望近似 πx,您需要将包含 π 的浮点区间乘以 x 的区间。如果您将 x 的间隔乘以表示为 3.1415926535897932 的双精度数,您将得到 x 乘以该双精度数的误差(既不等于 π 也不等于 3.1415926535897932)。在您问题的代码中,常量 0.4 表示“最接近 0.4 的双精度数”。如果您需要有理数 4/10,请使用以 3.9999999999999997e-014.0000000000000002e-01 分别表示的两个双精度为边界的区间。

【讨论】:

  • 所以使用 ULP 的想法是死路一条,或者?
  • @KristianSpangsege:基本上,是的。假设您有两个仅在 ULP 中不同的正数,并且已知它们在一个 ULP 中是正确的。如果添加它们,则结果在一个 ULP 内是正确的(新 ULP 的价值是旧 ULP 的两倍)。但是如果你减去它们呢?
  • @KristianSpangsege 将错误表示为单个浮点计算结果周围的宽度是可能的,但如果您希望正确执行,它的数学运算并不令人愉快。假设你有实数 n1 和 n2 表示为 (d1 + e1) 和 (d2 + e2)。 d1 和 d2 相乘的误差是 d1 * d2 的 ULP 的一半。但是你不想将 d1 和 d2 相乘,你想将 n1 和 n2 相乘。所以你必须写 (d1 + e1)*(d2 + e2) = d1*d2 + ... 并在新误差中包含先前近似值产生的复合误差。
  • @KristianSpangsege 既然您询问了 C++ 实现,以下文章将引用其中的几个。其中至少有一个必须允许我推荐的“舍入区间算术”。 ac.usc.es/arith19/sites/default/files/…
猜你喜欢
  • 2018-09-01
  • 1970-01-01
  • 2019-03-27
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 2016-03-31
  • 1970-01-01
  • 2015-06-12
相关资源
最近更新 更多