一 概念
原码、反码和补码是计算机中用于表示有符号整数的三种不同的编码方式。
原码(Sign-Magnitude):
- 原码是最简单直观的表示方法,最高位表示符号位,0代表正数,1代表负数,其余位表示数值部分。
- 例如,正数+5的原码为0000 0101,负数-5的原码为1000 0101。
反码(One’s Complement):
- 反码的表示方法是对于正数与原码相同,对于负数则是在原码的基础上,符号位不变,其他位取反(0变为1,1变为0)。
- 例如,正数+5的反码为0000 0101,负数-5的反码为1111 1010。
补码(Two’s Complement):
- 补码是计算机中最常用的表示有符号整数的方法,对于正数与原码、反码相同,对于负数,其补码是其反码加1。
- 例如,正数+5的补码为0000 0101,负数-5的补码为1111 1011。
补码表示方法具有以下优势:
- 能够实现加减法的统一处理,无需单独设计减法电路。
- 避免了0的两个表示(正零和负零),简化了运算规则。
- 在计算机硬件中,使用补码可以简化逻辑电路的设计。
总体来说,原码、反码和补码是用来表示有符号整数的不同编码方式,而补码在计算机中得到了广泛的应用。
特点总结
- 二进制的最高位是符号位:0 是正数,1是负数
- 正数的 原码、反码、补码 是一样的
- 负数的反码 = 原码符号位不变,其它位取反(0->1 ; 1->0)
- 负数的补码 = 负数的反码 + 1 ,负数的反码 = 负数的补码 - 1
- 0 的反码,补码都是0
- 在计算机运算的时候,都是以
补码的方式来运算
的 - 当看运算结果的时候,要看
原码
二 位运算符
java 中有 7 个位运算&、 |、 ^、 ~、 >>、 <<和 >>>
分别是 按位与& 按位或| 按位异或^ 按位取反~ 右移运算符>> 左移运算符<< 无符号右移运算符>>>
运算规则如下:
按位与&
:将两个操作数的每个位进行与运算,如果两个对应位都为1,则结果为1,否则为0。
int a = 5; // 二进制表示为 0000 0101
int b = 3; // 二进制表示为 0000 0011
int result = a & b; // 结果为 0000 0001,即1
按位或|
:将两个操作数的每个位进行或运算,如果两个对应位中有一个为1,则结果为1,否则为0。
int a = 5; // 二进制表示为 0000 0101
int b = 3; // 二进制表示为 0000 0011
int result = a | b; // 结果为 0000 0111,即7
按位异或^
:将两个操作数的每个位进行异或运算,如果两个对应位不同,则结果为1,否则为0。
int a = 5; // 二进制表示为 0000 0101
int b = 3; // 二进制表示为 0000 0011
int result = a ^ b; // 结果为 0000 0110,即6
按位取反~
对操作数的每个位进行取反操作,即0变为1,1变为0。
int a = 5; // 二进制表示为 0000 0101
int result = ~a; // 结果为 1111 1010,即-6
右移运算符 >>
:将操作数的所有位向右移动指定的位数,移出的位被丢弃,左边用符号位填充(对于正数则用0填充)。- 本质
20 / 2 / 2 = 5
原本的数 / 2^位移数
int a = 20; // 二进制表示为 0001 0100
int result = a >> 2; // 结果为 0000 0101,即5
左移运算符 <<
:将操作数的所有位向左移动指定的位数,右边用0填充。- 本质
5 * 2 * 2 * 2 = 20
原本的数 * 2^位移数
int a = 5; // 二进制表示为 0000 0101
int result = a << 3; // 结果为 0010 1000,即40
无符号右移运算符 >>>
:将操作数的所有位向右移动指定的位数,移出的位被丢弃,左边用0填充。
int a = -20; // 二进制表示为 1111 1111 1111 1111 1111 1111 1110 1100
int result = a >>> 2; // 结果为 0011 1111 1111 1111 1111 1111 1111 0110,即1073741814
三 代码演示
例子:计算
2&3
~-2
~2
2|3
2^3
的结果
public static void main (String[] args) {
System.out.print("================ 2&3:");
// 推导
// 1. 取 2 的原码: 00000000 00000000 00000000 00000010
// 2. 取 2 的反码: 00000000 00000000 00000000 00000010
// 3. 取 2 的补码: 00000000 00000000 00000000 00000010
// 4. 取 3 的原码: 00000000 00000000 00000000 00000011
// 5. 取 3 的反码: 00000000 00000000 00000000 00000011
// 6. 取 3 的补码: 00000000 00000000 00000000 00000011
// 7. 运算后的补码:00000000 00000000 00000000 00000010
// 8. 运算后的反码/原码/补码是一致的
System.out.println(2&3); // 2
System.out.print("================ ~-2:");
// 推导
// 1. 取 -2 的原码: 10000000 00000000 00000000 00000010
// 2. 取 -2 的反码: 11111111 11111111 11111111 11111101
// 3. 取 -2 的补码: 11111111 11111111 11111111 11111110
// 4. 运算后的补码: 00000000 00000000 00000000 00000001
// 5. 运算后的反码/原码/补码是一致的
System.out.println(~-2); //1
System.out.print("================ ~2:");
// 推导
// 1. 取 2 的原码: 00000000 00000000 00000000 00000010
// 2. 取 2 的反码: 00000000 00000000 00000000 00000010
// 3. 取 2 的补码: 00000000 00000000 00000000 00000010
// 4. 运算后的补码: 11111111 11111111 11111111 11111101
// 5. 运算后的反码: 11111111 11111111 11111111 11111100
// 6. 运算后的原码: 10000000 00000000 00000000 00000011
System.out.println(~2); // -3
System.out.print("================ 2|3:");
// 推导
// 1. 取 2 的原码: 00000000 00000000 00000000 00000010
// 2. 取 2 的反码: 00000000 00000000 00000000 00000010
// 3. 取 2 的补码: 00000000 00000000 00000000 00000011
// 4. 取 3 的原码: 00000000 00000000 00000000 00000011
// 5. 取 3 的反码: 00000000 00000000 00000000 00000011
// 6. 取 3 的补码: 00000000 00000000 00000000 00000011
// 7. 运算后的补码: 00000000 00000000 00000000 00000011
// 8. 运算后的反码/原码/补码是一致的
System.out.println(2|3); //3
System.out.print("================ 2^3:");
// 推导
// 1. 取 2 的原码: 00000000 00000000 00000000 00000010
// 2. 取 2 的反码: 00000000 00000000 00000000 00000010
// 3. 取 2 的补码: 00000000 00000000 00000000 00000010
// 4. 取 3 的原码: 00000000 00000000 00000000 00000011
// 5. 取 3 的反码: 00000000 00000000 00000000 00000011
// 6. 取 3 的补码: 00000000 00000000 00000000 00000011
// 7. 运算后的补码: 00000000 00000000 00000000 00000001
// 8. 运算后的反码/原码/补码是一致的
System.out.println(2^3); // 1
}
计算
1 >> 2
-1 >>2
1 << 2
-1 << 2
3 >>> 2
public static void main (String[] args) {
int a = 1 >> 2; // 0
// 推导
// 1. 取 1 的原码: 00000000 00000000 00000000 00000001
// 2. 1的反码补码一致: 00000000 00000000 00000000 00000001
// 3. 1 右移 2 位: 00000000 00000000 00000000 00000000
// 4.00000000 00000000 00000000 00000000 转成十进制为 0
System.out.println("a = " + a);
int b = -1 >> 2; // -1
// 推导
// 1. 取 -1 的原码: 10000000 00000000 00000000 00000001
// 2. 取 -1 的反码: 11111111 11111111 11111111 11111110
// 3. 取 -1 的补码: 11111111 11111111 11111111 11111111
// 4. -1 右移 2 位: 11111111 11111111 11111111 11111111
// 5. 补码转反码: 11111111 11111111 11111111 11111110
// 6. 反码转原码: 10000000 00000000 00000000 00000001
// 7. 结果为 -1
System.out.println("b = " + b);
int c = 1 << 2; // 4
// 推导
// 1. 取 1 的原码: 00000000 00000000 00000000 00000001
// 2. 1的反码补码一致: 00000000 00000000 00000000 00000001
// 3. 1 左移 2 位: 00000000 00000000 00000000 00000100
// 4.00000000 00000000 00000000 00000100 转成十进制为 4
System.out.println("c = " + c);
int d = -1 << 2; // -4
// 推导
// 1. 取 -1 的原码: 10000000 00000000 00000000 00000001
// 2. 取 -1 的反码: 11111111 11111111 11111111 11111110
// 3. 取 -1 的补码: 11111111 11111111 11111111 11111111
// 4. -1 左移 2 位: 11111111 11111111 11111111 11111100
// 5. 补码转反码: 11111111 11111111 11111111 11111011
// 6. 反码转原码: 10000000 00000000 00000000 00000100
// 7. 结果为 -4
System.out.println("d = " + d);
int e = 3 >>> 2;
// 推导
// 1. 取 3 的原码: 00000000 00000000 00000000 00000011
// 2. 3的反码补码一致: 00000000 00000000 00000000 00000011
// 3. 3 无符号右移 2 位: 00000000 00000000 00000000 00000000
// 4.00000000 00000000 00000000 00000000 转成十进制为 0
System.out.println("e = " + e);
}