一、计算机中存储的都是补码
java也是如此:
System.out.println(Integer.toBinaryString(2));
System.out.println(Integer.toBinaryString(-2));
运行结果:
10 11111111111111111111111111111110
由于整数默认int型,32位。正数的补码和原码都一样,高位的0默认不打印。因此是10。
-2
原码:1000 0000 0000 0000 0000 0000 0000 0010
反码:1111 1111 1111 1111 1111 1111 1111 1101 (在源码基础上符号位不变,其余位取反)
补码:1111 1111 1111 1111 1111 1111 1111 1110 (反码+1)
从上面的结论来看,java二进制数据是以补码来存储的。
二、运算也是在反码的基础上进行运算的
1. ~ 按位非(NOT)操作。每位取反。即原来是0的变成1,原来是1的变成0。
System.out.println(~ (-2)); //结果为1
分析:
step1 -> -2原码:1000 0000 0000 0000 0000 0000 0000 0010 step2 -> -2反码:1111 1111 1111 1111 1111 1111 1111 1101 step3 -> -2补码:1111 1111 1111 1111 1111 1111 1111 1110 step4 -> 对-2的补码进行取反即可得到~(-2)的补码 step5 -> ~(-2)补码:0000 0000 0000 0000 0000 0000 0000 0001 step6 -> 由于符号位为0表示正数,因此补码和源码一样。转换为十进制就是1
2. & 按位与(AND)操作。1和1为1,1和0为0,0和0为0。
System.out.println(-3 & 2); //结果为0
分析:
step1 -> -3的原码:1000 0000 0000 0000 0000 0000 0000 0011 step2 -> -3的反码:1111 1111 1111 1111 1111 1111 1111 1100 step3 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step4 -> 2的补码:0000 0000 0000 0000 0000 0000 0000 0010 step5 -> step3和step4进行与运算即可得-3&2的补码,结果是正数,补码和原码相同。 step6 -> -3&2补码: 0000 0000 0000 0000 0000 0000 0000 0000 step7 -> 转换为十进制就为0
3. | 按位或(OR)操作。1和1为1,1和0为1,0和0为0。
System.out.println(-3 | 2); //结果为-1
分析:
step1 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step2 -> 2的补码:0000 0000 0000 0000 0000 0000 0000 0010 step3 -> step1和step2进行或运算即可得到-3|2的补码 step4 -> -3|2补码: 1111 1111 1111 1111 1111 1111 1111 1111 step5 -> 由于结果是在补码的基础上运算得来个,最高位为1表示负数,负数的补码和原码不相同,因此需要将补码转换为原码。 step6 -> -3|2反码: 1111 1111 1111 1111 1111 1111 1111 1110(反码到补码是反码+1,因此补码到反码是补码-1) step7 -> -3|2原码: 1000 0000 0000 0000 0000 0000 0000 0001(符号位不变,反码取反) step7 -> 将step7的二进制转换为十进制,即为-1
4. ^ 按位异或(XOR)操作。不同为1,相同为0。1和1为0,1和0为1,0和0为0。
System.out.println(-3 ^ 2); //结果为-1
分析:
step1 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step2 -> 2的补码:0000 0000 0000 0000 0000 0000 0000 0010 step3 -> step1和step2做异或运算得到补码 step4 -> -3^2补码: 1111 1111 1111 1111 1111 1111 1111 1111 step5 -> 由于结果是负数,因此需要将运算得补码转换为原码。 step6 -> -3^2反码: 1111 1111 1111 1111 1111 1111 1111 1110(补码-1) step7 -> -3^2原码: 1000 0000 0000 0000 0000 0000 0001 0001(符号位不变,反码取反) step8 -> 将step7的二进制原码转换为十进制数,结果为-1
5. >> 右移位操作(var >> bit)。指定值所有位向右移动指定位数。低位舍弃,高位按符号位填充。没有溢出的情况下,相当于除以2的n次方。
System.out.println(-3 >> 3); //结果为-1
分析:
step1 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step2 -> 右移3位 step3 -> 右移后补码:1111 1111 1111 1111 1111 1111 1111 1111 step4 -> 高位为负数因此,高位补1,低位舍弃。负数要转换为原码 step5 -> 右移后反码:1111 1111 1111 1111 1111 1111 1111 1110(补码-1) step6 -> 右移后原码:1000 0000 0000 0000 0000 0000 0000 0001(符号位不变,反码取反) step7 -> step6转换为十进制值为-1
6. >>> 无符号右移位操作(var >>> bit)。指定值所有位向右移动指定位数。低位舍弃,高位补0。
System.out.println(-3 >>> 3); //结果为536870911
分析:
step1 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step2 -> 无符号右移3位,高位补0,低位舍弃。 step3 -> 右移后补码:0001 1111 1111 1111 1111 1111 1111 1111 step4 -> 正数的补码和原码一样。结果为536870911
7. << 左移位操作(var << bit)。指定值所有位向左移动指定位数。低位补0,高位保留符号位。没有溢出的情况下,相当于乘以2的n次方。
System.out.println(-3 << 3); //结果为-24
分析:
step1 -> -3的补码:1111 1111 1111 1111 1111 1111 1111 1101 step2 -> 左移3位,高位不变。低位补0 step3 -> 右移后补码:1111 1111 1111 1111 1111 1111 1110 1000 step4 -> 右移后反码:1111 1111 1111 1111 1111 1111 1110 0111 step5 -> 右移后原码:1000 0000 0000 0000 0000 0000 0001 1000 step6 -> 将step5转换为十进制即为-24