保持浮点插值的最大可能精度

Retaining maximum possible accuracy on float interpolation

我们知道点 X1 和 X2 有各自的点 Y1 和 Y2,所以我们可以计算任意 X 的 Y:

 X - X1    Y - Y1
------- = -------
X2 - X1   Y2 - Y1

我们可以从中得到简单的公式 (A):

Y = (X - X1) * (Y2 - Y1) / (X2 - X1) + Y1;

这在数学上应该是等价的 (B):

Y = (X - X1) / (X2 - X1) * (Y2 - Y1) + Y1;

对于整数数学公式,只要乘法 (X - X1) * (Y2 - Y1) 结果保持在类型范围内,A 的性能就会更好。公式 B 不起作用,因为如果 X1 <= X <= X2,则除法将始终等于 0.

对于浮点数,两者都应该有效,但我认为 B 会提供更好的准确性,因为乘法结果将保持较小。

  1. 我关于浮点精度的假设是否正确?

  2. 是否有一些我没有考虑到的浮点怪癖?

采用 IEEE 754 浮点表示法。

注 1:我对浮点数感兴趣,整数数学非常简单。

注2:FP公式上的变量可以有非整数值,但NaN和Infs不在问题范围内。

假设没有发生下溢或上溢,它们在精度上应该大致相当:乘法和除法都会产生相同的相对误差,并且由于误差大致是乘法的,所以执行操作的顺序会获胜差别很大。

如果您对所涉及的项的相对大小有所了解,则可以重新排列项,使减法准确无误,这可能会稍微减少误差。

解决以下 Y

 X - X1    Y - Y1
------- = -------
X2 - X1   Y2 - Y1

(A) 和 (B) 的行为相似:

(A)    Y = (X - offsetX) * deltaY / deltaX + offsetY;
(B)    Y = (X - offsetX) / deltaX * deltaY + offsetY;

如果点最初是整数,"B ... multiplication result will remain smaller." 可能成立,但在其他方面 |deltaX| |deltaY| 可能都小于 1,然后这个假设可能会失败。

要提高准确性,请考虑减去 2 个数字(或添加 2 个符号不同的相似数字)的效果。代码可以通过颠倒 point1 和 point2 的角色来选择 X1,Y1X2,Y2 作为偏移量。 选择最接近 X、Y 的偏移量将提高精度

With FP math, * and / stressing the exponential range allowed by FP number: 乘积的精度可以预期在数学上正确答案的一点点以内,但是范围可能溢出。

+- 强调精度:范围很少成为问题,但用于形成总和的有效数字可能会有很大的抵消。


如果所有坐标值最初都是整数,建议使用 2x 宽整数数学运算并得出最佳答案。

如果最终结果要整数化,确保代码使用iy = (int) round(Y);

一般来说,乘法和除法很少会导致显着的精度损失。因为这些是 浮点数 数字,具有用于比例和有效数字的单独字段,所以获得大的中间结果本身不是问题。 2e100/3e1002/3(出于所有意图和目的)同样准确。

另一方面,结果幅度远小于操作数的加法或减法是精度损失的更常见原因。

考虑到这一点,这两种形式基本上是等价的。如果您的数字是 'mainstream'(即乘法不会导致 over/underflow),那么这两种形式都不会遇到任何问题。如果你不能假设你的数字是主流的,那么你必须采取各种特殊的预防措施才能得到一个好的结果。

现在,与其考虑 (A) 和 (B) 这两种形式,我建议在 (A) 和 (C) 之间进行选择:

Y = (X - X1) * (Y2 - Y1) / (X2 - X1) + Y1; (A)
Y = (X - X2) * (Y2 - Y1) / (X2 - X1) + Y2; (C)

并选择第一个因子 X - X1X - X2 幅度较小的形式。这样,如果 Y 结果很小,就可以最大限度地减少精度损失。

例如,让我们使用

(X1,Y1) = (-100, -100)
(X2,Y2) = (0, 0)
X = 0.76

三位精度。然后我们得到 (A):

Y = (0.76 - -100) * (0 - -100) / (0 - -100) + -100
  = 101 * 100 / 100 - 100
  = 1

而对于 (C),我们得到:

Y = (0.76 - 0) * (0 - -100) / (0 - -100) + 0
  = 0.76 * 100 / 100 + 0
  = 0.76

因此,您问题的快速答案是:

  1. 中间结果的大小本身并不重要。这不是偏爱 (B) 而不是 (A) 的理由。

  2. 始终将加法和减法视为更可能导致精度损失的来源。