原码、反码、补码
说明:本片文章均以一个字节(8个比特位)作为说明
原码、反码、补码的说明
-
原码:十进制数据的二进制表现方式。
- 最左边的位(最高位)是符号位,0 表示正数,1 表示负数;
举例:
-
56,在二进制的表达是00111000,最左边是符号位,之后是数据。 -
数字 原码 3 00000011 2 00000010 1 00000001 0 00000000 或 10000000 -1 10000001 -2 10000010 -3 10000011
-
反码:为解决原码在负数加减运算中的问题(原码的直接加减会导致符号位参与运算,结果错误)
-
正数的反码与原码相同。
-
负数的反码是在原码基础上,符号位不变,数值部分(除符号位外的位)按位取反(0 变 1,1 变 0)。
举例:
-
-
56的原码是:00111000,反码是:00111000 -
-56的原码是:10111000,反码是:11000111
-
-
数字 原码 反码 3 00000011 00000011 2 00000010 00000010 1 00000001 00000001 0 00000000 或 10000000 00000000 或 11111111 -1 10000001 11111110 -2 10000010 11111101 -3 10000011 11111100
说明:为什么说为了解决原码不能计算负数。如果在正数环境下,在数学环境下
56加上1是57,在原码环境下00111000加1是00111001,将二进制原码转换为十进制是57,符合数学运算规律。但是如果来到负数环境,-56的原码是10111000,在数学环境下-56加上1是-55,在原码环境下10111000加上1是10111001,将二进制原码转换为十进制是-57,符合不数学运算规律。即原码在负数环境下,加减都会与真实数学环境相反,所以引入反码,以解决原码不能计算负数加减的问题。 -
-
补码:为解决正数负数跨0计算的问题(补码中
0只有一种表示)-
正数的补码与原码相同。
-
负数的补码在反码的基础上,反码(二进制反码)加1。
eg:
-
-
56的原码是:00111000,反码是:00111000,补码是00111000 -
-56的原码是:10111000,反码是:11000111,补码是11001000
-
-
数字 原码 反码 补码 3 00000011 00000011 00000011 2 00000010 00000010 00000010 1 00000001 00000001 00000001 0 00000000 或 10000000 00000000 或 11111111 00000000 -1 10000001 11111110 11111111 -2 10000010 11111101 11111110 -3 10000011 11111100 11111101 -128 无 无 10000000
-
特点:计算机中,数据的存储和计算都是以补码计算的。
说明:为什么解决了正数负数跨0计算的问题。因为在原码中,数字0有两种表现形式,分别为00000000和10000000,分别对应着+0和-0,而在反码中也存在两个0的表现形式,分别为00000000和11111111,也对应着+0和-0。但是这就存在一个问题,即如果反码中跨0计算,0被计算了两次,导致最后结果与实际数学结果小1(举例:-1+3,数学结果应该是2,而在反码中,11111110 +3即00000001,结果是-1)。引入补码后,0只有一种表现形式,即00000000,不存在两种。至于多出来的一位0,则规定给最小一位。比如byte类型,最小一位是-128。所以任何类型的最小一位是没有原码和反码的。
类型转换的进一步说明
-
隐式转换(类型自动提升):取值范围小的自动提升为取值范围为大的
public class Demo { public static void main(String[] args){ byte a = 10; int b = a; } }在这里,a是byte类型,可以容纳8个比特位,其补码是00001010,而被隐式转换(自动提升),则是在前面不足的位数补0。b是int类型,可以容纳32个比特位,则在多出来的比特位上自动补0,即00000000000000000000000000001010.
-
强制转换:取值范围大的强制转换为取值范围为小的
public class demo { public static void main(String[] args){ int a = 300; byte b = (byte)a; } }在这里,a是int类型,可以容纳32个比特位,其补码是00000000000000000000000100101100,而byte类型的b只能容纳8个比特位,则将多余比特位数据直接删除,留下最后8位比特位的数据,即00101100,而00101100是44的补码。
位运算符
位运算符是在二进制补码的层面直接进行计算的运算符。
请注意,由于隐式转换的存在,本片内容简化为1比特。
| 运算符 | 含义 | 运算规则 |
|---|---|---|
| & | 逻辑与 | 0 -> false ; 1-> true ; 同真为真 |
| |
逻辑或 | 0 -> false ; 1-> true ; 同假为假 |
| << | 左移 | 补码左移,低位补0 |
| >> | 右移 | 补码右移,高位补0或1 |
| >>> | 右移 | 补码右移,高位补0 |
-
逻辑与
位运算符逻辑与是在二进制补码形式进行计算。0为false(假),1为真(true),按位计算,每位上都为真,结果为真,反之为假。
public class Demo { public static void main(String[] args){ System.out.println(42 & 108); } }运行结果
40其中,42的补码是00101010,108的补码是01101100,按位逻辑与,如果每位上都为真,结果为真,反之为假,最后运算的补码是00101000,则是40。
-
逻辑或
位运算符逻辑或是在二进制补码形式进行计算。0为false(假),1为真(true),按位计算,每位上都为假,结果为假,反之为真。
public class Demo { public static void main(String[] args){ System.out.println(42 | 108); } }运行结果
110其中,42的补码是00101010,108的补码是01101100,按位逻辑与,如果每位上都为假,结果为假,反之为真,最后运算的补码是01101110,则是110。
-
左移
位运算符左移是在二进制补码向左移动,低位自动补0。
public class Demo { public static void main(String[] args){ System.out.println(16 << 2); } }运行结果
64其中,16的补码是00010000,向左移动两位则是01000000,则是64的补码。
提示:向左移动一位,则是在原数基础上乘以2。
-
右移
位运算符右移是在二进制补码向右移动,最高位根据原数的正负自动补0或者1,其余高位补0,原数为正,最高位补0,原数为负,最高位补1。
public class Demo { public static void main(String[] args){ System.out.println(16 >> 2); System.out.println(-16 >> 2); } }运行结果
4 -4其中,16的补码是00010000,向右移动两位则是00000100,则是4的补码。如果原数为负数,则在高位补1,使最终结果仍然保持为负数。
提示:向右移动一位,则是在原数基础上除以2。
-
无符号右移
位运算符右移是在二进制补码向右移动,高位强制补0。(与右移区别)
public class Demo { public static void main(String[] args){ System.out.println(16 >>> 2); System.out.println(-16 >>> 2); } }运行结果
4 1073741820在这里,16的补码是
00000000 00000000 00000000 00010000,右移两位高位强制补0则是00000000 00000000 00000000 00000100,即4。而-16的补码是11111111 11111111 11111111 11110000,左移两位高位强制补0则是00111111 11111111 11111111 11111100,即1073741820