看以下实例:
// 有问题的版本
#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-
本文暂时没有评论,来添加一个吧(●'◡'●)