以下均以byte类型存储8位为例
原码
5 0000 0101
-5 1000 0101
首位是符号位,0表示正,1表示负
反码
5 0000 0101
-5 1111 1010
正数的反码是本身
负数的反码是除了符号位,其余位取反;
补码
5 0000 0101
-5 1000 1011
正数的补码是本身
负数的补码是它的反码加1
移位
以下出现的二进制都是原码
5 << 1 即 0000 1010 即 10 = 5 * 2
5 << 2 即 0001 0100 即 20 = 5 * 2 * 2
5 >> 1 即 0000 0010 即 2 (丢失精度)
6 >> 1 0000 0110 移位后 0000 0011 即 3 = 6 / 2
-5 << 1 即 1000 1010 即 -10 = -5 * 2
-5 << 2 即 1001 0100 即 -20 = -5 * 2 * 2
-5 >> 1 即 1000 0010 即 -2 (丢失精度)
-6 >> 1 1000 0110 移位后 1000 0011 即 -3 = -6 / 2
正数移位时,左移n位,得到的结果是原来的值乘以2的n次方,右移是除以2的n次方,有时会出现丢失精度。
负数与正数的区别是首位的符号位不动。
负数的补码进行移位时,左移则右边补0,右移则左边补1。
运算符& 、|
&(念做与)、|(念做或)
1010&0110=0010
1010|0110=1110
&表示必须两个都是1,结果才是1,否则是0
|表示只要有一个0,结果就是0,否则是1
思考
补码的作用
在计算机进行二进制计算时,如果两个数字的符号位不同,则需要先进行取绝对值得到两个数中较大的那个,然后将大数减去小数,结果的符号以大数为准。过程较为复杂,那么如果可以只用加法运算来替换减法运算,则会提升很多效率。
通过对负数取补码,然后和正数进行加法运算。所以计算机中的负数是都是以补码的形式存储。
为什么负数的补码是原码按位取反再加一?
以钟表为例,指向6为基础,顺时针转动为正,逆时针转动为负
若要指针指向3,需要顺时针转动9(+9),6+9=15
也表示逆时针转动3(-3),6-3=3
以12为模,15-12=3 = 6-3=3,就是说15和3表示的含义是一样的,都表示3点,所以-3和+9是互为补数的。计算方式为模-数,即12-3=9,-3的补数就是9。
在二进制时,模是2,-1001与+0111是互为补数的,计算方式为2(0010)-1001=0111,由此可以得出:在二进制中,负数的补码是原码按位取反,再加1。
反码的作用
反码的主要作用就是原码求补码的中间过渡
应用场景
在jdk1.8中的HashMap.java,默认初始容量使用了移位
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
该类中也存在多处使用到&、|运算
if ((first = tab[i = (n - 1) & hash]) != null) {
进行该种运算时,需要先根据负数求出补码,再与正数计算。