专业的编程技术博客社区

网站首页 > 博客文章 正文

C|深入理解浮点数在值域范围内表示大整数的舍掉和舍入位的问题

baijin 2024-08-10 13:38:42 博客文章 17 ℃ 0 评论

看以下实例:

// 有问题的版本 
#include <stdio.h>
int main() {
    float sum = 0.0f;
    for (int i = 0; i < 10000; i++) 
        sum += i + 1;
    printf("Sum of 1~10000: %f\n", sum); 
    printf("Sum of 1~10000: %f\n",10001*5000.0);
    getchar();
    return 0;
}
/* output:
Sum of 1~10000: 50002896.000000
Sum of 1~10000: 50005000.000000
*/

我们知道,float使用1个符号位,8个阶码和23个尾码。

阶码采取移码,E = e + 2^(8-1)-1

其值域有:

Float(IEEE754) 32 -3.4028E+38 3.4028E+38

为什么会有这个情况呢?

问题出在尾数只有23位,当其全部用来表示整数时,只能表示32位,也就是2^24-12 = 16777215

当大于这个数字时,就存在丢弃和舍入位的可能。

看累加到多少开始有精度损失:

#include <stdio.h>
#define MM 5793
int main() {
    float sum = 0.0f;
    for (int i = 0; i < MM; i++) 
    {
        sum += i + 1;
        printf("%d %f\n",i,sum); 
    }
    printf("Sum of 1~%d: %f\n",MM,sum); 
    printf("Sum of 1~%d: %f\n",MM,(MM+1)*MM/2.0);
    getchar();
    return 0;
}
/* output:
……
5791 16776528.000000
5792 16782321.000000
Sum of 1~5793: 16782320.000000
Sum of 1~5793: 16782321.000000
*/

看下面实例的演示:

#include <stdio.h>
int main() 
{
    float sum =16777215.0f;
    printf("%f\n",sum);
    ++sum;
    printf("%f\n",sum);
    sum++;
    printf("%f\n",sum);
    sum++;
    printf("%f\n",sum);
    sum++;
    printf("%f\n",sum);
    sum++;
    printf("%f\n",sum);
    sum++;
    printf("%f\n",sum);
    getchar();
    return 0;
}
/*
16777215.000000
16777216.000000
16777217.000000
16777217.000000
16777217.000000
16777217.000000
16777217.000000
*/

到底是怎样舍掉和舍入的呢?再看下面的实例:

#include <stdio.h>
int main() {
    float sum =16777215;
    int si = sum;
    printf("%f\n",sum);  // 16777215.000000
    sum =16777216;
    printf("%f\n",sum);  // 16777216.000000
    sum =16777217;
    printf("%f\n",sum);  // 16777216.000000     // 舍掉1位
    sum =16777218;
    printf("%f\n",sum);  // 16777218.000000 
    sum =16777219;
    printf("%f\n",sum);  // 16777220.000000     // 舍入1位
    sum =16777220;
    printf("%f\n",sum);  // 16777220.000000
    sum =16777221;
    printf("%f\n",sum);  // 16777220.000000     // 舍掉1位
    getchar();
    return 0;
}

数值增大几倍:

#include <stdio.h>
int main() {
    float sum =97897215;
    int si = sum;
    printf("%f\n",sum);
    sum =97897216;
    printf("%f\n",sum);
    sum =97897217;
    printf("%f\n",sum);
    sum =97897218;
    printf("%f\n",sum);
    sum =97897219;
    printf("%f\n",sum);
    sum =97897220;
    printf("%f\n",sum);
    sum =97897221;
    printf("%f\n",sum);
    sum =97897222;
    printf("%f\n",sum);
    getchar();
    return 0;
}
/* output:
97897216.000000
97897216.000000
97897216.000000
97897216.000000
97897216.000000
97897216.000000
97897224.000000
97897224.000000
*/

float类型的97897222的阶码和尾码:

其存在舍入和舍掉位的位数 = 阶码-127-23。

同理,double类型也存在同样的情况,只是其起点更长。

下面这样写参数可能会转换为double,放到了寄存器,则看不到精度丢失。(release编译更会如此)

#include <stdio.h>
int main() {
    float sum =16777215;
    printf("%f\n",sum);
    printf("%f\n",++sum);
    printf("%f\n",++sum);
    printf("%f\n",++sum);
    printf("%f\n",++sum);
    getchar();
    return 0;
}
/*
16777215.000000
16777216.000000
16777217.000000
16777218.000000
16777219.000000
*/

上面将sum的类型从float改成double就不会存在上述问题。因为double的尾码高达52位。

-End-

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表