给定两个段端点A和B(二维),我想基于值t进行线性插值,即:
C = A + t(B-A)
在理想世界中,A,B和C应该是共线的。但是,我们在此处使用有限的浮点运算,因此会出现小的偏差。为了解决其他操作中的数值问题,我正在使用最初由Jonathan Shewchuk创建的强大的自适应例程。特别是,Shewchuk实现了一个方向函数orient2d
,它使用自适应精度来精确测试三个点的方向。
这里我的问题:是否有一个已知的程序如何使用浮点数学计算插值,以便它恰好位于A和B之间的线上?在这里,我不太关心插值本身的准确性,更关心最终的共线性。换句话说,只要满足共线性,只要C移动一点就可以了。
答案 0 :(得分:0)
无法满足请求。有A
和B
的值,除了t
是浮点数的0和1之外,没有lerp(A, B, t)
的值。
一个简单的单精度示例是x1 = 12345678.f
和x2 = 12345679.f
。无论y1
和y2
的值如何,要求的结果都必须在x
和12345678.f
之间有一个12345679.f
分量,并且两者之间没有单精度浮点数这两个。
但是,精确的插值可以表示为5个浮点值的总和(在2D情况下为矢量):一个用于公式的结果,一个用于每个操作中的错误[1],另一个用于将错误乘以t
。我不确定这对您是否有用。为简单起见,这是单精度算法的一维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标准并不能保证这一点,但是至少在支持SSE2 [2] [3]的处理器中,可以指示GNU gcc 编译器这样做。
保证(result + err1 + err2 + err3 + err4)
的算术加等于期望的结果;但是,不能保证这些数量的浮点加法将是准确的。
要使用上面的示例,exact_lerp(12345678.f, 12345679.f, 0.300000011920928955078125f, &err1, &err2, &err3, &err4)
返回结果12345678.f
,而err1
,err2
,err3
和err4
是{{ 1}},0.0f
,0.0f
和0.300000011920928955078125f
。确实,正确的结果是12345678.300000011920928955078125,它不能表示为单精度浮点数。
一个更复杂的示例:0.0f
返回exact_lerp(0.23456789553165435791015625f, 7.345678806304931640625f, 0.300000011920928955078125f, &err1, &err2, &err3, &err4)
,错误为2.3679010868072509765625f
,6.7055225372314453125e-08f
,8.4771045294473879039287567138671875e-08f
和1.490116119384765625e-08f
。这些数字加起来就是准确的结果,即2.36790125353468468550173374751466326415538787841796875,不能精确地存储在单精度浮点数中。
以上示例中的所有数字均使用其确切值而不是近似值书写。例如,不能将0.3精确地表示为单精度浮点数;最接近的精确值为0.300000011920928955078125,这是我使用过的精确值。
如果按此顺序计算2.66453525910037569701671600341796875e-15f
,则可能会得到一个在用例中被视为共线的近似值。也许值得一试。