庄子之棰 float 和 double 精度不同导致的误差

结论:计算小数时优先选 double,而不是 float

《庄子·天下》

一尺之棰,日取其半,万世不竭。

一米的棍子,一天砍掉一半,问第 n 天(1~20)时被砍掉的总长度是多少?类似的有小球落地反弹一半的路程,下面的代码求的是小球从 50 米高空落地反弹的路程,结果保留十位小数,代码看起来没啥问题,当输入 20 的时候结果是 149.9998321533,而把 float 都换成 double 后结果则变成了 149.9998569489,两者在第五位小数就开始不同了,是算错了吗?不是的,用 python 算出来的结果(下面第二个代码)是 149.99985694885254,float 和 double 的计算结果又比较相近,是什么导致了这一个问题?

通俗的来说是两者的精度导致的问题,误差一点点的积累下来的。float 单精度,能保留 7 位小数;double 双精度,能保留 15 位小数。也就是说小球反弹的高度不足 0.0000001 时,float 计算就会出问题了。下面第三个代码演示了这个问题

// 从 50 米高空落地反弹的路程
#include <stdio.h>
int main()
{
    float h = 50.0;
    float d = 2.0;
    float sum = 0;
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        sum = sum + h;
        h = h / d;  // 注意这里没有直接写 h=h/2
        sum = sum + h;
    }
    printf("%.10f", sum);

    return 0;
}
# 计算 20 次落地的路程
h=50.0
s=0
for _ in range(20):
    s+=h
    h/=2
    s+=h
print(s)    # 149.99985694885254

下面的代码运行结果是:

hg!=hm
3.123457 3.123457
3.123457 3.123457
3.123456789012346 3.123456716537476
#include <stdio.h>
int main()
{
    double hm = 3.1234567890123456;
    float hg = hm;
    if (hg == hm) {
        printf("hg==hm\n");
    } else {
        printf("hg!=hm\n");
    }
    printf("%lf %lf\n", hm, hg); // lf f 都默认输出 6 位小数,四舍五入
    printf("%f %f\n", hm, hg);
    printf("%.15lf %.15lf\n", hm, hg); // 超出精度就开始不对劲了
    return 0;
}

Refer

https://github.com/BackMountainDevil/c_qa

posted @ 2024-04-14 15:10  沙滩炒花蛤  阅读(21)  评论(0编辑  收藏  举报