坏消息
请求无法满足。对于某些A
和B
的值,除了0和1之外,没有其他值t
使得lerp(A,B,t)
为浮点数。
在单精度下的一个简单例子是x1 = 12345678.f
和x2 = 12345679.f
。无论y1
和y2
的值如何,所需结果必须具有介于12345678.f
和12345679.f
之间的x
分量,而这两者之间没有单精度浮点数。
(有点)好消息
然而,确切的插值值可以表示为5个浮点值(在2D情况下为向量)的总和:一个用于公式的结果,一个用于每个操作的误差[1],以及一个将误差乘以t
的值。我不确定这对您是否有用。以下是单精度中使用融合乘加计算产品误差的算法的1D C版本,以便简化:
#include <math.h>
float exact_sum(float a, float b, float *err)
{
float sum = a + b;
float z = sum - a;
*err = a - (sum - z) + (b - z);
return sum;
}
float exact_mul(float a, float b, float *err)
{
float prod = a * b;
*err = fmaf(a, b, -prod);
return prod;
}
float exact_lerp(float A, float B, float t,
float *err1, float *err2, float *err3, float *err4)
{
float diff = exact_sum(B, -A, err1);
float prod = exact_mul(diff, t, err2);
*err1 = exact_mul(*err1, t, err4);
return exact_sum(A, prod, err3);
}
为了使该算法正常工作,操作需要符合IEEE-754的最近舍入模式。这在C标准中并不保证,但GNU
gcc编译器可以被指示在支持SSE2的处理器上执行此操作[2][3]。
可以保证
(result + err1 + err2 + err3 + err4)
的算术加法等于所需的结果;然而,不能保证这些量的浮点加法是精确的。
使用上面的例子,
exact_lerp(12345678.f, 12345679.f, 0.300000011920928955078125f, &err1, &err2, &err3, &err4)
返回结果
12345678.f
,而
err1
、
err2
、
err3
和
err4
分别为
0.0f
、
0.0f
、
0.300000011920928955078125f
和
0.0f
。确实,正确的结果是12345678.300000011920928955078125,它无法用单精度浮点数表示。
更复杂的例子:
exact_lerp(0.23456789553165435791015625f, 7.345678806304931640625f, 0.300000011920928955078125f, &err1, &err2, &err3, &err4)
返回
2.3679010868072509765625f
,错误分别为
6.7055225372314453125e-08f
、
8.4771045294473879039287567138671875e-08f
、
1.490116119384765625e-08f
和
2.66453525910037569701671600341796875e-15f
。这些数字加起来等于精确结果,即2.36790125353468550173374751466326415538787841796875,不能在单精度浮点数中精确存储。
上述示例中的所有数字都使用其精确值而非近似值编写。例如,0.3无法用单精度浮点数精确表示;最接近的一个具有精确值0.300000011920928955078125,这是我使用的那个。
也许可以尝试计算
err1 + err2 + err3 + err4 + result
(按照此顺序),以获得在您的用例中被认为共线的近似值。
参考资料
C=A+t.(B-A)
,然后在C
周围搜索区域,选择最佳的C
,使得dot(C-A,B-A)/(|C-A|.|B-A|)
最接近于1。您还可以尝试cross(C-A,B-A)
最小(三角形面积最小)。对于这个计算,您可以使用每个值的2个双精度浮点数来增强精度,而无需将其用于所有点... - Spektreulp
大小即可。为了提高精度,您还可以使用相对坐标,使点(0,0,0)
成为A,B
或(A+B)/2
,您会惊讶于它能做到多少,请参见光线和椭球体交点精度改进。我根本看不到任何边缘情况,但是是的,您不能期望完全匹配,只能得到最佳匹配。 - Spektre+,-,<
,但这将需要for
循环,因此速度会较慢,为O(|AC|)
。 - Spektre