诸君又该学习了,今天我们继续来一睹浮点数的奥妙真容。
经过前面文章对整形提升相关的解释,我们都对整形和字符在内存空间上的储存已经有了大概的认知,那么现在我们就来好好讲讲浮点数在内存中的储存规则。
浮点数与整形储存的不同
#include<stdio.h> int main() { int a = 9; float* p = (float*)&a; printf("%d\n", a); printf("%f\n", *p); *p = 9.0; printf("%d\n", a); printf("%f\n", *p); return 0; }
那么我们现在可以思考一下这个结果证明了什么呢?
是不是就是证明了,我们浮点数和整形数据在内存中的储存方式是不一样的,而且float类型和int整形所占字节都是4个字节,这样更能体现int和float内存储存数据的不一样。那么下面我将展开说一下float在内存中到底如何储存数据的。
浮点数在内存中的储存规则
首先这里我们先看一个公式,这使我们等下更好的理解这块
V = (−1) ^S *M * 2^E,那这里我们可以来来看看这里的S,M,E到底是什么?
(-1)^S 用来取决这个浮点型数据的正负,如果为0则为正数,如果为1则为负数。
M和E 即为一个大于1小于2的数,即1<=M<2,那么后面的2^E也很容易知道了,其为一个像科学计数法似的指数。如:5.5转化二进制浮点型表示------>101.1=1.011*2^2 那么此时M=1.001,E=2,S=0
我们这里直接甩图,让大家直接看清楚,浮点型数据中32位bit是怎么储存,打印时又要怎么读取呢。
float型
double型
这样一看诸君是否就清楚许多,恍然大悟呢,那么先别急,这里还需讲一个知识点。
浮点数的存储过程:
浮点数存的过程 IEEE 754 对有效数字M和指数E,还有⼀些特别规定。 前面说过, 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。 IEEE 754 规定,在计算机内部保存M时,默认这个数的第⼀位总是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第⼀位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第⼀位的1舍去以后,等于可以保 存24位有效数字。
至于指数E,情况就比较复杂 首先,E为⼀个无符号整数(unsigned int) 这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我 们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上 ⼀个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是 10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
总结:计算机储存的M是直接省略整数为的1的,对于指数E我们在其基础上加上127或是1023在存取到计算机内存中,至于是加上哪个看是单精度float还是double。
下面我们看一个例子:
我们继续拿float a=5.5这个数来举例。
这里按照上面的说法。5.5为正数,那么S=0。
然后将5.5换成二进制=101.1,在换成科学计数法=1.011*2^2,此时M=1.011,然后在计算机中储存时把整数为的1,暂时舍去,在取出来时在加回去。那么此时计算机中的
M=01100000……(23bit),然后到我们这里的E=2。再按照之前的说法在这基础上加上中间数127在储存在计算机中,那么此时计算机中的E=2+127=129,转换为二进制=10000001
然后a在计算机中的储存为0 10000001 01100000000000000000000
然后二进制表示为40 b0 00 00,
那么是否就是这样呢,我们来用计算机来验证一下
这里VS是小端储存所以我们就看到如图的储存方式
浮点数取的过程
E不全为0或不全为1 这时,浮点数就采⽤下⾯的规则表⽰,即指数E的计算值减去127(或1023),得到真实值,再将有效 数字M前加上第⼀位的1。 ⽐如:0.5 的⼆进制形式为0.1,由于规定正数部分必须为1,即将⼩数点右移1位,则为1.0*2^(-1),其 阶码为-1+127(中间值)=126,表⽰为01111110,⽽尾数1.0去掉整数部分为0,补⻬0到23位 00000000000000000000000,则其⼆进制表⽰形式为:
0 01111110 00000000000000000000000
E全为0 这时,浮点数的指数E等于1-127(或者1-1023)即为真实值,有效数字M不再加上第⼀位的1,而是还 原为0.xxxxxx的⼩数。这样做是为了表⽰±0,以及接近于0的很小的数字。
0 00000000 00100000000000000000000
E全为1 这时,如果有效数字M全为0,表示±⽆穷大(正负取决于符号位s)
0 11111111 00010000000000000000000
最后再回到开始的代码:
#include<stdio.h> int main() { int a = 9; float* p = (float*)&a; printf("%d\n", a); printf("%f\n", *p); *p = 9.0; printf("%d\n", a); printf("%f\n", *p); return 0; }
这里我们一步一步拆分,在这段代码中int a=9.则是按整形的方式来储存,则二进制在计算机中储存为 00000000000000000000000000001001,那么在进行这么(printf("%f\n", *p);)打印时我么会把他当作浮点数的形式进行取出,此时S=0,E为全0按照上面的取出规则
E=1-127。计算机中的M=00000000000000000001001,在加上存之前舍去的1,则M=100000000000000000001001,这么计算起来M*2^-126,非常小,接近于0,所以打印出来一个
0.000000
然后到第三行打印结果,我们将9已浮点数的形式储存,浮点数9.0 等于⼆进制的1001.0,即换算成科学计数法是:1.001×2^3 所以: 9.0 = (−1) ^ 0 *(1.001) ∗ 2^3 , 那么,第⼀位的符号位S=0,有效数字M等于001后⾯再加20个0,凑满23位,指数E等于3+127=130, 即10000010 所以,写成⼆进制形式,应该是S+E+M,即0 10000010 001 0000 0000 0000 0000 0000
那么按照整形的方式取出来就是一个很大的数,即1091567616
文章已到末尾,诸君对浮点数储存懂否。