前言不知道读者在刚接触编程时有没有想过 将 int型数据 按浮点数类型打印会怎样,如下代码:
int x = 7;
printf("%d ", x);
printf("%f", x);
在不理会编译器提醒的情况下,得到的结果:一个是7,一个是0.000000。
Why?
一、浮点数在内存中的存储和读取规则1.浮点数,包括float、double、long double等类型,根据国际标准规定,任意⼀个⼆进制浮点数V可以表⽰成下⾯的形式:
V = (−1)^S ∗ M ∗ 2^E;F
(1)(−1)^S 表示符号位,当VS=0时,F表示正数;当S=1时,F为负数。
(2)M为有效数字,为1<=M<2之间;
(3)2^E表示位数。
2.占用内存情况:S符号占1个二进制位,接着是E占8个二进制位(如果是64位浮点数则占11位),再然后才是M占23位二进制位(如果是64位浮点数占52位)。
3.存储规则:
(1)M:由于浮点数已经写成二进制形式,故1<=M<2,当编译器存储M时会把整数部分的1省略掉,全部用来存取小数点后的位数,在读取时自动加上1,这样做的目的是为了增加精度。
(2)E:由于在科学计数法中指数也是有负数形式的,以在E中存储的值会加上127(或1023)以便存储负数形式的指数,再存储在内存中。
4.读取规则:
(1)E中不全为0或不全为1:先将E中存储的数值减去127(或1023)得到真实值,再把M中的值填充到“1.”之后,最后依据公式得出该数的二进制形式,最后转换为十进制数。
(2)E中全为0:如果再减去127后的得到的真实值非常小,故此时计算机不再将M中的1加入,直接将M中的值加到“0.”后,以表示±0,或接近0的数。
(3)E中全为1:此时再看M中的值,若全为0则表示为无穷大(小),若不是就按正常“E中不全为0或不全为1”还原。
二、举列现在我们站在float的视角,看看前言中的int x按照“%f”类型是怎么打印的。int x = 7;
x为正数在内存中按二进制方式存储:00000000 00000000 00000000 00000111,
现在我们站在float的视角读取内存中的内容:
先取第一位S位,为0,故该数是个整数。
再取8位E,全为0,M不加1,则该数为0.xxx的形式。
最后取剩下的23位M还原为十进制后,直接加到小数点后
还原成公式形式:V=(-1)^0 * 0.00000000000000000000111 * 2^0 ,换算成十进制后再取前6位(32位浮点数形式),打印出的必然是0.000000.
再举列float y = 0.5:
由于存储规则,整数部分必须为1,所以化为二进制后为1.0 * 2^(-1),则S=0,E=-1(126的二进制011111110),M=1;
即为0 01111110 00000000000000000000000(别忘了编译器会自动加上1),因在vs中地址采用16进制编码,故该二进制转十六进制为 3F 00 00 00,在vs调试内存窗口中可以看到为0x000000B1142FF7B4 00 00 00 3f。
(为什么存储顺序不同可参看文章”)。
再举列-5.0,写成二进制数为-101,相当于- 1.01×2^2 ,按照公式可写为(-1)^1 * 1.01 * 2^2,故S=1,M=1.01,E=2。
“纸上得来终觉浅”,还是得动手敲一敲才能彻底明白、吸收。