保持浮点插值的最大可能精度
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 会提供更好的准确性,因为乘法结果将保持较小。
我关于浮点精度的假设是否正确?
是否有一些我没有考虑到的浮点怪癖?
采用 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,Y1
或 X2,Y2
作为偏移量。 选择最接近 X、Y 的偏移量将提高精度。
With FP math, *
and /
stressing the exponential range allowed by FP number: 乘积的精度可以预期在数学上正确答案的一点点以内,但是范围可能溢出。
+
和 -
强调精度:范围很少成为问题,但用于形成总和的有效数字可能会有很大的抵消。
如果所有坐标值最初都是整数,建议使用 2x 宽整数数学运算并得出最佳答案。
如果最终结果要整数化,确保代码使用iy = (int) round(Y);
一般来说,乘法和除法很少会导致显着的精度损失。因为这些是 浮点数 数字,具有用于比例和有效数字的单独字段,所以获得大的中间结果本身不是问题。 2e100/3e100
和 2/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 - X1
或 X - 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
因此,您问题的快速答案是:
中间结果的大小本身并不重要。这不是偏爱 (B) 而不是 (A) 的理由。
始终将加法和减法视为更可能导致精度损失的来源。
我们知道点 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 会提供更好的准确性,因为乘法结果将保持较小。
我关于浮点精度的假设是否正确?
是否有一些我没有考虑到的浮点怪癖?
采用 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,Y1
或 X2,Y2
作为偏移量。 选择最接近 X、Y 的偏移量将提高精度。
With FP math, *
and /
stressing the exponential range allowed by FP number: 乘积的精度可以预期在数学上正确答案的一点点以内,但是范围可能溢出。
+
和 -
强调精度:范围很少成为问题,但用于形成总和的有效数字可能会有很大的抵消。
如果所有坐标值最初都是整数,建议使用 2x 宽整数数学运算并得出最佳答案。
如果最终结果要整数化,确保代码使用iy = (int) round(Y);
一般来说,乘法和除法很少会导致显着的精度损失。因为这些是 浮点数 数字,具有用于比例和有效数字的单独字段,所以获得大的中间结果本身不是问题。 2e100/3e100
和 2/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 - X1
或 X - 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
因此,您问题的快速答案是:
中间结果的大小本身并不重要。这不是偏爱 (B) 而不是 (A) 的理由。
始终将加法和减法视为更可能导致精度损失的来源。