首先先讲下:float与double类型的内存分布,精度和范围
内存分布:
C/c++的浮点数据类型有float和double两种。
float大小为4字节,内存中的存储方式如下:
符号位(1bit)指数(8bit)尾数(23bit)
double大小为8字节,内存中的存储方式如下:
符号位(1bit)指数(11bit)尾数(52bit)
符号位决定浮点数的正负,0正1负。
指数和尾数均从浮点数的二进制科学计数形式中获取。
如,十进制浮点数2.5的二进制形式为10.1,转换为科学计数法形式为(1.01)*(10^1)。
由此可知指数为1,尾数(即科学计数法的小数部分)为01。
根据浮点数的存储标准,指数用移码表示。0的float类型移码为127(0111 1111),0的double类型移码为1023(011 1111 1111)。运算时,在0的移码基础上加指数,得到的就是内存中指数的表示形式。尾数则直接填入,如果空间多余则以0补齐,如果空间不够则0舍1入。所以float和double类型分别表示的2.5如下(二进制):
符号位 指数 尾数
0 1000 0000 010 0000 0000 0000 0000 0000
0 100 0000 0000 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
精度:
float和double的精度是由尾数的位数来决定的。
float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;
double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。
范围:
float类的指数是8位移码,最大为127最小为-127,127用来作2的指数,为2^127,约等于 1.7014*10^38, 而我们知道,floa示数范围约为-3.4*10^38-------3.4*10^38, 这是因为尾数都为1时,即1.11..11约为2,因此浮点数的范围就出来了.double的情况与float完全相似.
接下来切入主题:
我们先看下这道题目,http://acm.pku.edu.cn/JudgeOnline/problem?id=1306
这道题目的大概意思就是求排列组合,先把代码贴出来:
#include<stdio.h>
int main()
{
int i,N,M;
double Sum;
while (scanf("%d %d",&N,&M)!=-1&&N&&M)
{
Sum=1;
for(i=1;i<=M;i++)
{
Sum*=(N-i+1);
Sum/=i;
}
printf("%d things taken %d at a time is %.0lf exactly./n",N,M,Sum);
}
return 0;
}
然后我们在看下这道题目:
http://acm.hdu.edu.cn/showproblem.php?pid=1002
我们用相同的方法试下,代码如下:
#include<stdio.h>
int main()
{
int T,i;
double a,b;
while(scanf("%d ",&T)!=-1)
{
for(i=1;i<=T;i++)
{
scanf("%lf %lf",&a,&b);
printf("Case %d:/n",i);
printf("%.0lf + %.0lf= %.0lf/n/n",a,b,a+b);
}
}
}
当我们输入112233445566778899 998877665544332211时,打印出如下
Case 1:
112233445566778900 + 998877665544332160= 1111111111111111000
咦,怎么搞的,怎么超出了,double的范围不是很大吗,怎么超出了呢?
上面的题目怎么没有超出呢?
原因是这样的,我们直接赋值会溢出,但第一题就不会溢出。