首先我们知道常用科学计数法是将所有的数字转换成(±)a.bx10c的形式,其中a的范围是1到9共9个整数,b是小数点后的所有数字,c是10的指数。而计算机中存储的都是二进制数据,所以float存储的数字都要先转化成(±)a.b x 2c,由于二进制中最大的数字就是1,所以表示法可以写成(±)1.b x 2c的形式,float要想存储小数就只需要存储 (±),b和c 这三个数据就可以了。
float的存储正是将4字节32位划分为了3部分来分别存储正负号,小数部分和指数部分的:
- Sign(1位):用来表示浮点数是正数还是负数,0表示正数,1表示负数。
- Exponent(8位):指数部分。即上文提到数字c,但是这里不是直接存储c,为了同时表示正负指数以及他们的大小顺序,这里实际存储的是c+127。
- Mantissa(23位):尾数部分。也就是上文中提到的数字b。
三部分在内存中的分布如下,用首字母代替类型
S | E | E | E | E | E | E | E | E | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 1 |
float存储示例
以数字168.25为例,看一下这个数字是怎么存储在float变量中的:
先来看整数部分168,模2求余可以得到二进制表示为10101000。
再来看小数部分0.25,乘2取整可以得到二进制表示为 .01
拼接在一起得到10101000.01然后写成类似于科学计数法的样子,得到1.010100001*27
从上面的公式中可以知道符号为正,尾数是010100001,指数是7。
符号为正,那么第一位填0,指数是7,加上偏移量127等于134,二进制表示为10000110,填到2-9位,剩下的尾数010100001填到尾数位上即可
S | E | E | E | E | E | E | E | E | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M | M |
0 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
内存中二进制数01000011 00101000 01000000 00000000表示的就是浮点数 168.25
float范围
明白了上面的原理就可求float类型的范围了,找到所能表示的最大值,然后将符号为置为1变成负数就是最小值,
要想表示的值最大肯定是尾数最大并且指数最大,那么可以得到尾数为 0.1111111 11111111 11111111,指数为 11111111,但是指数全为1时有其特殊用途,所以指数最大为 11111110,指数减去 127 得到 127,所以最大的数字就是1.1111111 1111111 11111111 x 2127,这个值为 340282346638528859811704183484516925440,通常表示成 3.4028235E38,那么float的范围就出来了:
[-3.4028235E38, 3.4028235E38]