浮点数存储格式学习:找到非规格数中最大和最小的数
在学习《深入理解计算机系统》的浮点数存储里,我想到一个问题,单精度浮点数中,非规格浮点数最大数是几?
00000000011111111111111111111111
符号位:0
指数域:00000000
小数域:11111111111111111111111
指数域全0,小数域全1,符号位0,这个数应该是非规格数中最大的一个了,那么这个数存在吗?计算机是这样存储的吗?
单精度浮点数:符号位1个,指数位8个,小数位23个,偏置值bias=127,E=1-bias=-126,M=f=(0.11111111111111111111111)2
根据存储定义:浮点数V=(-1)sM2E,V=(0.11111111111111111111111)2 * 2-126
让我们开始编程查看一下吧:
#include <cstdio> int main(){ float a=0.5,c=0.5;
//第一个for是将a的值累加到(0.111...11),注意0.5在二进制下就是0.1 for(int i=0;i<22;i++){ c*=0.5; a=a+c; }
//第二个for将a和2-126相乘。 for(int i=0;i<126;i++){ a*=0.5; } unsigned int b=*(int *)&a; for(int i=0;i<32;i++){ printf("%d",(b>>(31-i))&1?1:0); } printf("\nb=%d\n",b); return 0; }
输出:
vagrant@ubuntu-bionic:~$ ./t 00000000011111111111111111111111 b=8388607 //震惊,连b的值都有意义了,8388607=223-1
解释:第一个for中,将循环次数设为22,是因为a本身有一个1,只需再加22个就够了。更有意思的是:如果把次数改为23,则全0,也就是浮点数的0了,如果改为24,那么就是规格数中最小的一个喽,呵呵,有意思,终于理解《深解》说的平滑过渡啥意思了。
那么这个最大的非规格浮点数到底是多少呢?在上面程序加上一句:
printf("%.200f\n",a);
输出:
vagrant@ubuntu-bionic:~$ ./t 00000000011111111111111111111111 a=0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875000000000000000000000000000000000000000000000000000
最后面的0可以删除,这样是为了看着方面,确认没数了。这个数最大的非规格浮点数是:
a=0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875
数字前面有37个0,a*1038约等于1.1755,这还是非规格数里面最大的一个,晕。
那么最小的非规格数是多少呢?基本和前面同理:
#include <cstdio> int main(){ float a=0.5; for(int i=0;i<22;i++){ a*=0.5; } for(int i=0;i<126;i++){ a*=0.5; } unsigned int b=*(int *)&a; for(int i=0;i<32;i++){ printf("%d",(b>>(31-i))&1?1:0); } printf("\na=%.200f\n",a); printf("\nb=%d\n",b); return 0; }
输出:
vagrant@ubuntu-bionic:~$ ./t1 00000000000000000000000000000001 a=0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125000000000000000000000000000000000000000000000000000 b=1
这就是浮点数,如果你懂它,它也很精确的。