【问题标题】:floating point number imprecision while iterating迭代时浮点数不精确
【发布时间】:2012-11-22 12:42:20
【问题描述】:

我有一个函数可以根据[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);
}

【问题讨论】:

  • 嗯,你能用十进制吗?
  • binary floating-point number cannot represent exactly 1 are you sure? 1.) 小数在 2 的基数中具有无限小数位存在问题 2.) 数字太小而无法精确表示 3.) 数字太大而无法在不损失精度的情况下表示。但是 1.0 都不是!
  • “我面临的问题是,二进制浮点数不能精确地表示 1。”
  • 对不起,我认为它不能代表 1,在研究之前不应该这样做。请查看我的编辑。
  • 您可能听说过EPSILON,而不是ELLEPSIS

标签: c floating-point floating-accuracy


【解决方案1】:
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;

【讨论】:

  • t = k * 0.1f 是完美的。非常感谢:)
  • 如果我看到了你的回答,我不会花太多精力在我的上面。
  • @ppeterka Computing i / 10.0 在您的答案中与i * 0.1f 相比,在此答案中给出了更好的错误分布,但每次迭代都会以浮点除法为代价。第一个区别是9 * 0.1f,比9 / 10.0f稍微错误一点。 ideone.com 显然没有 long double 的扩展精度,但在一个平台上运行这个程序会显示:ideone.com/XDRzrO。当我运行它时,我得到 0xe.68887cccc8aeeedp-7 的乘法误差和 0xc.599996665a0cccdp-7 的除法。
  • @PascalCuoq 错误的分布是我把答案留在那里的原因,但你是对的,我什至没有考虑过 FP 部门的性能影响——这不是值得关注的事情大迭代......在这种情况下,我总是会尝试使用两种方法的幂 - 也考虑负幂,就像它们一样,位移运算符可用于除法和乘法。
【解决方案2】:

二进制浮点数不能正好表示 1

Proof that it can can be found here.

最准确的表示 = 1.0E0

可能有问题

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

但是 1.0 都不是!

然而 0.1是个问题案例,违反了第1点,看this

最准确的表示 = 1.00000001490116119384765625E-1

因此,如果将 0.1 相加十次,您将得到大于 1.01.00000001490116119384765625E-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 语句中使用了额外的花括号。这样做,总有一天你会感谢自己的。)

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 啊,是的,它是 EPSILON 而不是 ELLIPSIS :D 谢谢你的例子,总有一天会有用的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    相关资源
    最近更新 更多