储存,在平时写代码时并不会过多注意的一个点。
知道这篇文章,实际上就是在修炼自己的内功。
一、整数的储存
在谈浮点数的储存时,我们先讲回整数的储存。
我们知道整数的储存和正负有关系。
正整数的原码、反码、补码都相同。
而负整数的储存则是补码(原码取反+1就可得到,同时补码取反+1就得到原码)。
一共32位,左起第一位为符号位,而剩余的为数值位。
范围就是-2^32~2^32-1。非常庞大的数字范围。
二、浮点数的储存
说回正题,浮点数的储存和整数储存有什么相同之处吗?答案是没有什么共同之处,除了都得用二进制以外。
(V就是浮点数)(图示为float类型,double类型的E为11bit,其余52个比特位留给M)
根据IEEE(电气电子工程师学会)规定,M虽然是一个1~2之间的数字,但是在储存时不会储存整数部分的1,只会储存小数部分,这相当于增加了精度,同时也扩大了范围。
有同学可能会好奇M部分是如何储存的,毕竟我们之前一直都学的是1、2、4这种。
M部分的储存仍是二进制,这点不变。但是左起的第一位变成了2^-1,也就是说,越往右越小,均为2的负数次方。
也就是说float类型的储存方式是符号位+指数位+有效数字位。
储存范围比int大?
当你掐指一算,如果指数部分全取1,那么E=63,可以知道float的最大值大约会是2^63~2^64这个量级。而int小了一半。
一个问题逐渐浮现出来,为什么我们不用float这种储存方式?比int高效啊(同为32位)
这个时候就不得不指出个中奥秘——精度问题。
不妨试试几个数字。
如果我们想储存4,这个时候只需S=0,E=2,M=1便可以轻松做到。
如果我们想储存6,这个时候只需S=0,E=2,M=1.5便可以轻松做到。
如果我们想储存99,这个时候只需S=0,E=8,M=1.546875。
意识到问题了吗?M的小数位在变长,而且我们储存的数据其实并不大。
事实上,范围内数据并不能完全用浮点数的方式表示完全,而整数的储存方式保证了可以完全包括范围内整数。
储存精度
float的储存精度并没有想象中这么高,我们随便编几个小数位略多的数字,你就会发现有几个很难通过23位小数得到(要记住,M的第1位是0.5或0,第2位是0.25或0,这是定好的)
所以计算机在比较两个float类型的数字时,容许了计算误差,这也是为什么我们偶尔会发现数字被打印成了xx.9999的形式。这也是double类型存在的意义,“双精度”三个字不是浪得虚名。
但是在实际操作中,我们还是需要注意空间问题,在需要精度高的地方使用double,不需要尽量用float。
这就和long long、long、int、short的使用考量一样。