3

我有一个函数可以根据 range 中的值计算 3d 中的点[0, 1]我面临的问题是,二进制浮点数不能完全表示 1。

在函数中计算的数学表达式能够计算 的值t=1.0,但该值永远不会被函数接受,因为它会在计算之前检查是否为范围。

curves_error curves_bezier(curves_PointList* list, curves_Point* dest, curves_float t) {
    /* ... */
    if (t < 0 || t > 1)
        return curves_invalid_args;
    /* ... */
    return curves_no_error;
}

我怎样才能用这个函数计算 3d 点t=1.0ELLIPSIS前段时间我听说过一些事情,我认为与这样的问题有关,但我不确定。

谢谢

编辑:好的,对不起。由于我面临的问题,我假设浮点数不能完全代表 1。问题可能是因为我正在做这样的迭代:

for (t=0; t <= 1.0; t += 0.1) {
    curves_error error = curves_bezier(points, point, t);
    if (error != curves_no_error)
        printf("Error with t = %f.\n", t);
    else
        printf("t = %f is ok.\n", t);
}
4

3 回答 3

8
for (t=0; t <= 1.0; t += 0.1) {

您的问题是二进制浮点数不能准确表示0.1.

最接近的 32 位单精度 IEEE754 浮点数是 0.100000001490116119384765625,最接近的 64 位双精度浮点数是 0.1000000000000000055511151231257827021181583404541015625。如果严格按照 32 位精度进行算术运算,则0.1f0 加 10 次的结果为

1.00000011920928955078125

如果以更高的精度执行中间计算float,则可能会导致精确1.0或什至略小的数字。

要解决您的问题,在这种情况下,您可以使用

for(k = 0; k <= 10; ++k) {
    t = k*0.1;

因为10 * 0.1f正是1.0

另一种选择是在你的curves_bezier函数中使用一个小的公差,

if (t > 1 && t < 1 + epsilon) {
    t = 1;
}

对于适当小的 epsilon,也许float epsilon = 1e-6;.

于 2012-11-22T12:56:13.223 回答
4

二进制浮点数不能完全表示 1

证明可以在这里找到

最准确的表示 = 1.0E0

可能有问题

  1. 以 2 为基数的具有无限小数位的小数
  2. 数字太小而无法准确表示而不会丢失精度
  3. 太大而无法在不损失精度的情况下表示的数字。

但是1.0没有一个!

但是, 0.1是一个问题案例,违反了第 1 点,请看:

最准确的表示 = 1.00000001490116119384765625E-1

因此,如果将 0.1 相加十次,您将得到1.00000001490116119384765625E-0大于1.0.

(示例为 IEEE754 单精度 32 位浮点数)

可能的解决方案:

int i;
for (i=0; i <= 10; i++) {
    t=i/10.0;
    curves_error error = curves_bezier(points, point, t);
    if (error != curves_no_error) {
        printf("Error with t = %f.\n", t);
    }
    else {
        printf("t = %f is ok.\n", t);
    }
}

这样,二进制格式的错误就不会被总结!

注意:if我在and语句中使用了额外的花括号else。这样做,有一天你会感谢自己的。)

于 2012-11-22T12:55:09.273 回答
2

在比较浮点数时,您应该检查它们是否足够接近而不完全相等,原因在其他答案中提到,例如:

#define EPSILON 0.000001f
#define FEQUAL(a,b) (fabs((a) - (b)) < EPSILON)
于 2012-11-22T13:00:01.690 回答