目录
因为CPU只有加法器,所以对于整型来说,数据在内存中通常采用补码的方式进行储存。
在这里复习一下原码、反码、补码。
正数和无符号数的原码、反码、补码相同;
负数的原码是把数字按二进制直接翻译,反码是原码除符号位按位取反,补码是反码+1 。
int a = 20;
// 0000 0000 0000 0000 0000 0000 0001 0100 —— 原码
// 0000 0000 0000 0000 0000 0000 0001 0100 —— 反码
// 0000 0000 0000 0000 0000 0000 0001 0100 —— 补码
// 0x00 00 00 14
int b = -10;
// 1000 0000 0000 0000 0000 0000 0000 1010 —— 原码
// 1111 1111 1111 1111 1111 1111 1111 0101 —— 反码
// 1111 1111 1111 1111 1111 1111 1111 0110 —— 补码
// 0x ff ff ff f6
我们所熟知的int类型通常在内存中占用4个字节的空间,而short类型占用2个字节,char类型则使用1个字节的空间。所以我们敏锐的察觉到,既然空间有限,那么总会有空间存不下的数字,这时候怎么办呢?
这便引出了溢出的问题。我们以char类型为例子,来讨论一下数字变化规律。
我们从中可以发现一些比较实用的规律:
①-1的补码二进制位全为1;
②对于一个有符号整数家族的类型,其所能表示的最小的负数为符号位取1,其余位取0;
③溢出发生在所能存储的最大值变为所能存储的最小值;
任意一个二进制浮点数可以表示为该形式: (-1)^S * M * 2^E
(-1)^S 表示符号位,S=0为正数,S=1为负数;
M表示有效数字;
2^E表示指数位。
例如,5.0二进制是101.0,相当于1.01 * 2^2。得到S=0,M=1.01,E=2 。
以上便是二进制的科学计数法的表示,可以类比我们熟悉的十进制的科学计数法来学习。
对于32位的浮点数(float),最高的1位是符号位S,紧接着8位是指数E,剩下的23位是有效数字M。
对于64位的浮点数(double),最高的1位是符号位S,紧接着11位是指数E,剩下的52位是有效数字M。其内部存储方式与float大同小异,区别在于double的偏置值位1023,即储存E时要在实际指数的基础上加1023.
小结一下:
1)M一定为1.xxxxxx,所以默认舍去1,只保留xxxxxx这一部分。如1.01只保存01;
2)E有可能为负数,所以存入E时会加上一个中间数,E为8位时中间数为127,E为11位时中间数位1023。如2^10,保存为32为浮点数时,保存为10+127=137,即1000 1001。
3)E从内存中取出分为三种情况:
①E不全为0或不全为1:E减去127(或1023),在M前加上第一位的1。
②E全为0:此时E的真实值为-127(-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数,以表示+-0或接近于0很小的数字。
③E全为1:此时如果有效数字M全为0,表示+-无穷大。