三流码奴的自我救赎

0%

Java_基本数据类型&二进制表示

bit(比特)&byte(字节)

bit叫做或者比特,在计算机中数据都是以01存储的,一个bit就对应一位数字
byte叫做字节,是比bit更高位的计量单位

  • 1byte = 8bits

java中的基本数据类型

catagory bytes bits 范围($2^n$)
boolean 1 8 $(-2)^7$ ~ $2^7-1$
char 2 16 $(-2)^{15}$ ~ $2^{15}-1$
—- —- —- —-
byte 1 8 $(-2)^7$ ~ $2^7-1$
short 2 16 $(-2)^{15}$ ~ $2^{15}-1$
int 4 32 $(-2)^{31}$ ~ $2^{31}-1$
float 4 32 $(-2)^{31}$ ~ $2^{31}-1$
long 8 64 $(-2)^{63}$ ~ $2^{63}-1$
double 8 64 $(-2)^{63}$ ~ $2^{63}-1$

仔细观察可以发现除了较为特殊的booleanchar之外,其余的类型按占空间从小到大排列,占用控件为:

$2^8$ -> $2^{16}$ -> $2^{32}$ -> $2^{64}$

对于char,在java中无论要表示哪种字符,一个char一定占用2bytes,因为在java中使用Unicode字符集进行utf-16可变长编码,而utf-16形如:u\8D63,可以看到一个utf-16字符占用了4个十六进制数,所以一个char只会占用2bytes的空间

这也就解释了为什么一个emoji会占用掉2个char,因为一个utf-16无法完整表示一个emoji表情

为什么1个byte是8bits缺只能表示$2^7$量级?

要弄懂这个问题就非常有必要研究一下计算机对数据的编码方式
原码,反码,补码共同导致了一个byte的表示区间限制在$(-2)^7$ ~ $2^7-1$中

原码,反码,补码

为了方便电路逻辑运算,在计算机中只有加法而没有减法,为了实现减法则需要把人类认知的减法转换成计算机中的加法:
1-1 = 1+(-1)
因为符号的存在,所以需要在二进制位中特殊化一个bit作为符号标志位,一个bit只可能为0或者1,正好满足表示正负的需求

所以在计算机中,一个8bit数1的原码会被表示成: 原 -> 000000001,而-1会被表示为原 -> 10000001

原码

所谓源码就是没有经过任何处理的原始编码:
以8bit数字1-1举例:

1
2
(1)  -> 原码 -> 0 000 0001
(-1) -> 原码 -> 1 000 0001

反码

既然可以表示负数了,那么是不是意味着直接将1-1的编码按位相加就能得到正确的结果呢?
其实不然:

1
2
3
     (1) -> 原码 -> 0 000 0001
(-1) -> 原码 -> 1 000 0001
(1)+(-1) -> 原码 -> 1 000 0010 == Oct(-2)

心里一个大写的 WTF,这就意味着需要计算机进行额外的操作来规避正负数相加产生的问题
但是留给逻辑电路的运算方法选择已经不多了,无非就是&& || !和他们之间的组合,其中&&||是二元运算符,一定会引入其他的操作数,这样以来结果会变得不可预知,所以剩下来的操作就只有!

对于二进制运算我们应该知道,一个n bits二进制数·x按位做!运算的话,结果等于 $2^n-x$,也就是说一个8bits表示的有符号数字-1按位做!运算结果为-126,而-126-1正好等于-127(有符号8bits最大值),而继续套用上面的公式,对-127!运算就能直接得到结果-0,正数同理可以得到结果+0

到这一步就已经明朗了,如果能得到一个相对最大值互补的结果,对它进行!操作就能得到正确的值,则对于1-1的例子,不难发现对-1!后与1相加,再做!就能得到正确的结果:

1
2
3
4
     (1) -> 原码 -> 1 000 0001
(-1) -> 反码 -> 1 111 1110
(1)+(-1) -> ?? -> 1 111 1111 == -127
取反 -> 1 000 0000 = (-0)

但源码和反码相加会导致类型的矛盾,所以约定:
正数的反码等于原码,负数的反码为源码除符号位取反
这样就变成了:

1
2
3
4
     (1) -> 反码 -> 1 000 0001
(-1) -> 反码 -> 1 111 1110
(1)+(-1) -> 反码 -> 1 111 1111 == -127
原码 -> 1 000 0000 = (-0)

DONE !

补码

问题虽然解决了,但并不完美
不难发现,在计算过程中0这个数存在两种表示+0-0,数学老师跟我说过0没有符号,这个解决方式显然不够完美
所以这个时候补码就出现了,补码就是在反码的基础上+1,但正数的补码和原码相同:

1
2
3
4
5
6
7
     (1) -> 补码 ->   0 000 0001
(-1) -> 补码 -> 1 111 1111
(1)+(-1) -> 补码 -> 1 0 000 0000 == 0

(-1) -> 补码 -> 1 111 1111
(-127) -> 补码 -> 1 000 0001
(1)+(-1) -> 补码 -> 1 1 000 0000 == (-128)

经过这样的计算,最高位因进位被舍弃,原先的-0就变成了-128,这就是正区间会少一个数的原因

PERFECT DONE !