一、问题背景
博主在准备应聘的笔试、面试时,再次采用了多年以来的Java工具书《Java疯狂讲义》,当博主看到该书的《Java疯狂讲义》第3章(数据类型和运算符)
时,里面涉及了原码、反码和补码之间符号位改变的特殊情况,因此博主想完全地探讨其全部的特殊情况。
二、原码、反码和补码
当用二进制来表示一个数时,最高位是符号位,符号位是0表明它是一个正数,符号位是1表明它是一个负数。
计算机都是通过正数、负数的补码来运算,而不是正数、负数的原码。
例如用8位二进制来表示-128~127的127
时,-128 ~ 127的原码、反码和补码表格如下:
1.原码、反码和补码之间的运算规则
1.1正数的原码、反码和补码之间的运算规则
一个正数,按照绝对值大小转换成的二进制数就是正数的原码。
正数的原码与其反码和补码都相同。
例如用8位二进制来表示-128~127的127
时(实际例子:Java的Byte类型),127的原码、反码和补码都是01111111
。
1.2负数的原码、反码和补码之间的运算规则
一个负数,按照绝对值大小转换成的二进制数,然后最高位补1,就是负数的原码。
负数的反码为对该负数的原码除符号位外各位取反。
负数的补码为对该负数的原码除符号位外各位取反,然后在最后一位加1。
补码的设计目的是:
- 使符号位能与有效值部分一起参加运算,从而简化运算规则。
- 使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。
上述图片的负数的原码、反码和补码之间的运算规则分别提到了反码最低位+1,+1时符号位可能进位,绝不借位
和补码最低位-1,-1时符号位不可能借位,绝不进位
,它们的解释如下:
反码最低位+1,+1时符号位可能进位,绝不借位
是指:特殊情况下的[-0]反码1111 1111
在+1后因为符号位发生了进位,得到[-0]补码1 0000 0000
。而反码最低位+1
是加法操作,故绝不会有借位操作。补码最低位-1,-1时符号位不可能借位,绝不进位
是指:特殊情况下的[-128]补码1000 0000
在-1反推其[-128]反码0111 1111
时会出现符号位借位的情况。但是编程领域规定:在用8位二进制来表示-128~127的一个数时,-128仅有补码而没有对应的反码、原码,所以特殊情况下的[-128]补码反推其反码导致的符号位借位情况是不可能出现的。而补码最低位-1
是减法操作,故绝不会有进位操作。
例如用8位二进制来表示-128~127中的-3
时(实际例子:Java的Byte类型),首先将-3
的绝对值+3
转换成二进制数0000 0011
,然后将其最高位+1,就得到-3
的原码1000 0011
。-3
的原码1000 0011
在除符号位外其余位取反得-3
的反码1111 1100
。-3
的反码1111 1100
在+1后得到-3
的补码1111 1101
。
[+3]原码=0000 0011
[-3]原码=1000 0011
[-3]反码=1111 1100
[-3]补码=1111 1101
1.3正零(+0
)和负零(-0
)的原码、反码和补码
因为是正的,所以正零+0
的原码与其反码和补码都相同;而因为是负的,负零(-0
)的原码、反码和补码之间都不相同。
[+0]原码=0000 0000, [-0]原码=1000 0000
[+0]反码=0000 0000, [-0]反码=1111 1111
[+0]补码=0000 0000, [-0]补码=0000 0000
由上可见,+0和-0的补码是一样的。即0的补码只有一种表示。
这里解释一下[-0]补码是怎么得来的: [-0]原码1000 0000
在除符号位外按位取反后得到[-0]反码1111 1111
,[-0]反码1111 1111
再+1后因为符号位发生了进位,得到[-0]补码1 0000 0000
。 9位二进制的[-0]补码1 0000 0000
已经超出了8位二进制的位数限制,那只能保留最低的8位二进制,即最高位1
要被舍弃,因此最后[-0]补码还是0000 0000
。
因此+0和-0的补码是一样的,补码能表示的数的个数比原码、反码各少了一个,所以补码可以多表示一个真值为-128的数。
但是,多表示的这个数-128比较特殊,只有补码,没有原码和反码。
1.4特殊负数-128的原码、反码和补码
编程领域规定:在用8位二进制来表示-128~127的一个数时,-128仅有补码而没有对应的反码、原码。
1.5二进制符号位(最高位)改变的特殊情况
整个编程领域中,二进制符号位(最高位)改变的特殊情况主要为以下3种:
- 在负数原码与负数反码互相转换时的取反操作中,符号位不变。
- 负数反码+1得负数补码时,符号位可能进位。但负数补码-1得负数反码时,符号位不可能借位。
特殊情况下的[-0]反码
1111 1111
在+1后因为符号位发生了进位,得到[-0]补码1 0000 0000
。
特殊情况下的[-128]补码1000 0000
在-1反推其[-128]反码0111 1111
时会出现符号位借位的情况。但是编程领域规定[-128]仅有补码,[-128]是没有对应的反码、原码的,所以特殊情况下的[-128]补码反推其反码导致的符号位借位情况是不可能出现的。
- 位运算符的按位非
~
: 单目运算符,将操作数的每个位(包括符号位)全部取反。
如程序执行
~-5
的结果是4
,其运算原理如下:
参考文献:
[1]关于 -128 ,+128,-0,+0,-1 的反码补码
[2]+0或者-0的源码、反码、补码
[3]8位原码反码补码表
[4]补码10000000为什么可以表示-128?
[5]为什么-0的补码是00000000?
[6]正数负数的原码、反码、补码
[7]终于搞懂你了——补码!