正数和负数的原码反码补码及二进制位运算
Posted passzhang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正数和负数的原码反码补码及二进制位运算相关的知识,希望对你有一定的参考价值。
位运算符
位:二进制简称“位”,是二进制计数系统中表示小于 2 的整数符号,一般用 1 或 0 表示,是具有相等概率的两种状态中的一种。二进制的位数可表示一个机器字的字长,一个二进制位包含的信息量称为 1 bit。(摘自百度百科)
位运算符用来对二进制位进行操作,Java中提供了如下所示的位运算符(操作数只能为整型和字符型数据):
& 按位与
| 按位或
^ 按位异或
~ 按位取反
除 ~ 以外,其余均为二元运算符。
一个字节 = 8bit(位)
一个 int 类型的数值占 32bit(位)
二进制换算示例
int i = 123;
10 进制 123 转为二进制后等于:1111011
完整补位后:
00000000 00000000 00000000 01111011
tips:全文使用的二进制为 32 位。
原码、反码和补码
二进制的最高位为符号位,1 表示负数,0 表示整数,其余位表示数的绝对值。
123 转为二进制补齐后为:00000000 00000000 00000000 01111011,这是 123 的原码。
负数 -123 的原码:100000000 00000000 00000000 01111011。
其中首位是符号位,0代表正数,1代表负数。
反码:正数的反码和原码相同,负数的反码是保留其符号位不变,其他位取反,(0 变 1,1 变 0)。
补码:正数的补码和原码相同,负数的补码为原码除符号位外取反 +1。
注意:整数在计算机中,是以补码的形式存储的。
正数对比
123 的原码:00000000 00000000 00000000 01111011
123 的反码:00000000 00000000 00000000 01111011
123 的补码:00000000 00000000 00000000 01111011
负数对比
-123 的原码:10000000 00000000 00000000 01111011
-123 的反码:11111111 11111111 11111111 10000100
-123 的补码:11111111 11111111 11111111 10000101
已知补码求原码
最高位如果是 1 的话(负数),那么除了最高位之外的取反,然后加 1 得到原码。
最高位如果是 0 的话(正数), 不变,正数的补码就是它的原码。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。(摘自百度百科)
打个比方:2-1是怎么计算的?
二进制计算规则采用11得1,10得1,00得0 逢2进1
计算公式:2-1=2+(-1);
2 的补码:00000010
-1 的补码:11111111
-1 的原码:10000010
-1 的反码:11111101
结果 0 00000001,最高位溢出(0)丢弃, 2-1 = 1。
关于原码、反码和补码的详细解释,可看这篇:
https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
为何要使用原码, 反码和补码
在开始深入学习前, 我的学习建议是先"死记硬背"上面的原码, 反码和补码的表示方式以及计算方法.
现在我们知道了计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 既然原码才是被人脑直接识别并用于计算表示方式, 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头). 但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
如果用原码表示, 让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用原码表示一个数.
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
补码的加减法运算
本文内容参考自王达老师的《深入理解计算机网络》一书<中国水利水电出版社>
一、补码加法:
1、补码的加法运算
两个机器数相加的补码可以先通过分别对两个机器数求补码,然后再相加得到,在采用补码形式表示时,进行加法运算可以把符号位和数值位一起进行运算(若符号位有进位,导致了益出,则直接舍弃),结果为两数之和的补码形式。
示例1:求两个十进制数的和 35+18。
首先,规定字长是8位,也就是只能用8位二进制表示。
35的原码:00100011。
18的原码:00010010。
因为35和18都是正数,所以补码和原码完全一致。
35的补码:00100011。
18的补码:00010010。
因为补码是可以连同符号位一起运算,所以运算法则等同于无符号二进制运算:
00100011---35二进制表示
00010010---18二进制表示
00110101-----转换成10进制是53。结果正确!
示例2:求两个十进制数的和 35+(-18)。
同示例1一样,只能用8位表示。
35的原码:00100011。
-18的原码:10010010。
因为35是正数,补码与原码完全一致,但是-18是负数,补码需要转换。
35的补码:00100011。
-18的补码:由原码除符号位外取反,再在最低位+1,得到结果是11101110。这时都是补码,运算规则等同于无符号二进制加法。
00100011
11101110
100010001---因为前面规定了字长是8位,这里出现了9位,
所以最高位舍弃,舍弃后,结果为00010001,
转换成十进制是:17。结果正确!(超出字长部分直接舍弃)
二、补码减法:
1、补码的减法运算
减法实际上就是加一个负数。运算法则和加法实际上是一致的!
————————————————
版权声明:本文为CSDN博主「不去上课」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ruidianbaihuo/java/article/details/88056298
以上是关于正数和负数的原码反码补码及二进制位运算的主要内容,如果未能解决你的问题,请参考以下文章