迭代时浮点数不精确

时间:2012-11-22 12:42:20

标签: c floating-point floating-accuracy

我有一个函数可以根据范围[0, 1]中的值计算3d间距。 我面临的问题是,二进制浮点数不能正好代表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;
}

如何使用此功能计算t=1.0处的3d点?我听说过一段时间ELLIPSIS的一些事情,我认为这与这个问题有关,但我不确定。

由于

编辑:好的,对不起。我假设一个浮点数不能正好代表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);
}

3 个答案:

答案 0 :(得分:8)

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

您的问题是二进制浮点数不能完全代表0.1

最接近的32位单精度IEEE754浮点数为0.100000001490116119384765625,最接近的64位双精度值为0.1000000000000000055511151231257827021181583404541015625。如果严格按32位精度执行算术,则将0.1f十次加到0的结果是

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;

答案 1 :(得分:4)

  

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

Proof that it can can be found here

  

最准确的表示= 1.0E0

可能存在问题
  1. 在基数为2
  2. 时具有无限小数位的小数
  3. 数字太小而不能精确表示而不会失去精确度
  4. 数字太大而无法在不丢失精度的情况下表示。
  5. 1.0 不属于他们!

    然而 0.1 是一个问题,违反第1点,请查看this

      

    最准确的表示形式= 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);
        }
    }
    

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

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

答案 2 :(得分:2)

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

#define EPSILON 0.000001f
#define FEQUAL(a,b) (fabs((a) - (b)) < EPSILON)