/*
* 2021/2/4 15:33 qing
*/
/*
* float 存储
*/
浮点型变量在计算机内存中占用4字节(Byte),即32-bit。遵循IEEE-754格式标准。
一个浮点数由2部分组成:底数 m 和 指数 e
±mantissa × 2exponent /* 尾数×2指数 */
(注意,公式中的mantissa 和 exponent使用二进制表示)
底数部分 使用2进制数来表示此浮点数的实际值。
指数部分 占用8-bit的二进制数,可表示数值范围为 0 - 255 。
但是指数应可正可负,所以IEEE规定,此处算出的次方须减去 127 才是真正的指数。所以float的指数可从 -126 到 128.
底数部分实际是占用 24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有 23-bit。
到目前为止, 底数部分 23位 加上指数部分 8位 使用了31位。那么前面说过,float是占用4个字节即 32-bit,那么还有一位是干嘛用的呢?
还有一位,其实就是 4 字节中的最高位,用来指示浮点数的正负,当最高位是 1 时,为负数,最高位是 0 时,为正数。
浮点数据就是按下表的格式存储在 4 个字节中:
Address+0 Address+1 Address+2 Address+3
Contents SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM S: 表示浮点数正负,1为负数,0为正数
E: 指数加上127后的值的二进制数
M: 24-bit的底数(只存储 23-bit)
主意:这里有个特例,浮点数为 0 时,指数和底数都为 0 ,但此前的公式不成立。 因为 2 的 0 次方为 1 ,所以, 0 是个特例。
当然,这个特例也不用认为去干扰,编译器会自动去识别。
/*
* -12.5在计算机中存储的具体数据
*/
Address+0 Address+1 Address+2 Address+3
Contents 0xC1 0x48 0x00 0x00
由于浮点数不是以直接格式存储,他有几部分组成,所以要转换浮点数,首先要把各部分的值分离出来。
Address+0 Address+1 Address+2 Address+3
格式 SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
二进制 11000001 01001000 00000000 00000000
16进制 0xC1 0x48 0x00 0x00
S:为 1 ,是个负数。
E:为 10000010 转为 10 进制为 130 , 130 -127 = 3 ,即实际指数部分为 3.
M:为 10010000000000000000000 。 这里,在底数左边省略存储了一个 1 ,使用 实际底数表示为 1.10010000000000000000000
把三个部分的值都拎出来了,现在,通过指数部分 E 的值来调整底数部分 M 的值。
调整方法为:如果指数E为负数,底数的小数点向左移,如果指数E为正数,底数的小数点向右移。小数点移动的位数由指数E的绝对值决定。
这里,E为正3,使用向右移3为即得: 1100.10000000000000000000
这个结果就是 12.5 的二进制浮点数,将他换算成 10 进制数就看到 12.5 了,转换如下:
小数点左边的 1100 表示为 (1 × 8) + (1 × 4) + (0 × 2) + (0 × 1), 其结果为 12 。
小数点右边的 .100… 表示为 (1 × 2^(-1)) + (0 × 2^(-2)) + (0 × 2^(-3)) + ... ,其结果为 .5 。
以上二值的和为 12.5 , 由于 S 为 1 ,使用为负数,即 -12.5 。
所以,16进制 0XC1480000 是浮点数 -12.5 。
/*
* 内存数据: 01000001001000100000000000000000
*/
第一步:符号位为 0 ,表示是正数;
第二步:指数位为 10000010 ,换算成十进制为 130 ,所以指数为 130 - 127 = 3;
第三步:尾数位为 01000100000000000000000 ,换算成十进制为 (1+1/4+1/64);
所以相应的十进制数值为: 2^3 * (1+1/4+1/64) = 8+2+1/8 = 10.125
/*
* 浮点数装换成计算机存储格式中的二进制数
*/
将 17.625 换算成 float型
将 17.625 换算成二进制位: 10001.101 ( 0.625 = 0.5 + 0.125, 0.5 即 1/2, 0.125 即 1/8)
再将 10001.101 向右移,直到小数点前只剩一位 成了 1.0001101 x 2 的 4 次方(因为右移了4位)。此时底数 M 和指数 E 就出来了:
底数部分M,因为小数点前必为1,所以IEEE规定只记录小数点后的就好,所以此处底数为 0001101 。
指数部分E,实际为 4 ,但须加上 127 ,固为 131 ,即二进制数 10000011
符号部分S,由于是正数,所以S为 0.
综上所述, 17.625 的 float 存储格式就是: 0 10000011 00011010000000000000000
转换成16进制:0x41 8D 00 00
还是占用了4个字节
/*
* 8.25和120.5在内存中真正的存储方式
*/
8.25 用二进制的科学计数法表示为
:1.0001 * 2^3 按照上面的存储方式,符号位为 0 ,表示为正;指数位为 3 + 127 = 130,位数部分为 1.00001,
故 8.25 的存储方式如下: 0xbffff380: 01000001000001000000000000000000
分解如下: 0--10000010--00001000000000000000000
符号位为 0 ,指数部分为 10000010 ,位数部分为 00001000000000000000000
120.5在内存中的存储格式如下: 0xbffff384 : 01000010111100010000000000000000
分解如下 : 0--10000101--11100010000000000000000
/*
* 小数部分转换成二进制数
*/
/*
* 十进制的小数到二进制的转换
*/
十进制的小数转换为二进制,主要是小数部分乘以 2 ,取整数部分依次从左往右放在小数点后,直至小数点后为 0 。
例如十进制的 0.125 ,要转换为二进制的小数。
将小数部分0.125乘以2,得0.25,然后取整数部分 0
0.125 * 2 = 0.25 取 0
再将小数部分0.25乘以2,得0.5,然后取整数部分 0
0.125 * 2 = 0.25 取 0
0.25 * 2 = 0.5 取 0
再将小数部分0.5乘以2,得1,然后取整数部分 1
0.125 * 2 = 0.25 取 0
0.25 * 2 = 0.5 取 0
0.5 * 1 = 1 取 1
则得到的二进制的结果就是 0.001
/*
* 二进制到十进制的转换
*/
二进制的小数转换为十进制主要是乘以 2 的负次方,从小数点后开始,依次乘以 2 的负一次方, 2 的负二次方, 2 的负三次方等。
例如二进制数 0.001 转换为十进制。
第一位为0,则0*1/2,即0乘以2负 一次方。
小数点后
第一位 0 * 1/2 即乘以 2 的负一次方
第二位为0,则0*1/4,即0乘以2的负二次方。
小数点后
第一位 0 * 1/2 即乘以 2 的负一次方
第二位 0 * 1/4 即乘以 2 的负二次方
第三位为0,则0*1/4,即0乘以2的负三次方。
小数点后
第一位 0 * 1/2 即乘以 2 的负一次方
第二位 0 * 1/4 即乘以 2 的负二次方
第三位 1 * 1/8 即乘以 2 的负三次方
各个位上乘完之后,相加, 0 * 1/2 + 0 * 1/4 + 1 * 1/8 得十进制的 0.125
= 0 + 0 + 0.125
= 0.125