原码、反码、补码和位运算符详解

前言

在这里插入图片描述

位运算一直以来都是程序设计中的一个难点,原码、反码、补码让人混乱,涉及进制知识更让初学者一脸懵逼,今天我打算好好梳理一下原码、反码、补码以及位运算的知识点

要做位运算得知道二进制是什么,首先我们知道计算机存储数据都是采用的二进制,二进制只有0和1两个符号表示,逢二进一,例如十进制1就是二进制1,十进制2就是进一位也就是二进制10, 十进制3就是二进制11,4就是100,以此类推,而位运算就是以二进制来运算的,这里不在进制方面更多的深入,只做简单的介绍。

再然后得知道原码、反码、补码什么,因为网上很多对其介绍太过复杂,我打算从位运算符入手,介绍每个位运算符的使用和计算过程,用到相关知识后在提出并加粗,文章最后再总结这些知识点

以Java来举例,Java中一共七个位运算符(&,|,^,~,>>,<<,>>>),为了方便介绍,我把它们分为两两一组,最后三个为一组,现在我们先说第一组

第一组:按位与:&,按位或: |

它们的运算规则分别是

  1. 按位与:& ,全为1,结果为1,否则为0
  2. 按位或: | ,有一个为1,结果就为1,否则为0

可能现在看不懂,没关系,继续走

1,按位与:&

现在我们来做一个题,计算一个13&7的结果

计算第一步我们要得到它们的二进制数,现在就涉及到原码、反码、补码的第一个知识点,①二进制的最高位是符号位,0表示整数,1表示负数,怎么理解呢举个例子,13的32位二进制数应该是00000000 00000000 00000000 00001101,因为是正数所以它的最高位是0,如果首位是1的话就是-13

然后通过简单的计算或计算器获取它们的二进制数,这也可以理解为它们的原码

// 13 = 00000000 00000000 00000000 00001101
// 7  = 00000000 00000000 00000000 00000111

开始计算之前涉及到了第二个知识点 ②在计算机计算的时候,都是采用的补码的方式,因此我们需要在原码的基础上获得它的补码,然后是第三个知识点③正数的原码、反码、补码是一样的,刚好我们的两个数字都是正数,所以可以直接计算

//按位与&,都为1则为1,不然为0,对每一位进行比较都是1结果就是1,不然就是0
// 13    = 00000000 00000000 00000000 00001101
// 7     = 00000000 00000000 00000000 00000111
//result = 00000000 00000000 00000000 00000101 我们得到了这个结果

计算完了之后是第四个知识点④运算结果要看原码,因为我们的两个数字都是正数,原码、反码、补码都一样,所以不用处理,直接得到101的十进制数为:5,因此13&7 = 5,用代码验证一下计算结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oXsTWv2J-1635940847031)(C:\Users\MoWang\AppData\Roaming\Typora\typora-user-images\image-20210824230859260.png)]

2,按位或: |

再来看按位或: | ,有一个为1,结果就为1,否则为0,同样做个题推导一下 -5|7 ,这次来试试负数

之前提到过正数的原码、反码、补码是一样的,而负数则比较麻烦,要得到负数的补码,首先要得到负数的反码,⑤负数的反码是最高位的符号位不变,其他位取反,得到反码之后再根据负数反码补码的关系 ⑥负数的补码 = 它的反码 + 1,反之负数的反码 = 它的补码 - 1

//按位或| ,有一个为1,结果就为1,否则为0
// -5    = 10000000 00000000 00000000 00000101 原码
// -5    = 11111111 11111111 11111111 11111110 反码 最高位不变其他位取反
// -5    = 11111111 11111111 11111111 11111111 补码 反码+1
// 7     = 00000000 00000000 00000000 00000111
//result = 11111111 11111111 11111111 11111111 结果补码 
//result = 11111111 11111111 11111111 11111110 结果反码 补码-1
//result = 10000000 00000000 00000000 00000001 结果原码 最高位不变其他位取反

我们之前说过,计算的结果要看原码,所以在得到负数的补码之后,还要经过反向操作把负数变为原码,才是我们要的结果,因此-5|7 = -1,用代码验证一下计算结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qg8UI4Ec-1635940847033)(C:\Users\MoWang\AppData\Roaming\Typora\typora-user-images\image-20210824233441510.png)]

第二组:按位异或:^,按位取反:~

它们的运算规则分别是

  1. 按位异或:^,两个不同结果为1,相同为0
  2. 按位取反:~ ,取反操作 0变1,1变0
3,按位异或:^

同样的我们来做一个题,计算一个3&-3的结果

//按位异或:^,两个不同结果为1,相同为0
// -3    = 10000000 00000000 00000000 00000011 原码
// -3    = 11111111 11111111 11111111 11111100 反码 最高位不变其他位取反
// -3    = 11111111 11111111 11111111 11111101 补码 反码+1
//  3    = 00000000 00000000 00000000 00000011
//result = 11111111 11111111 11111111 11111110 结果补码
//result = 11111111 11111111 11111111 11111101 结果反码 补码-1
//result = 10000000 00000000 00000000 00000010 结果原码 最高位不变其他位取反 = -2

我相信到认真看到这应该可以自己独立的进行推导了吧,推荐可以自己设计几个数字来测试一下自己的推导过程是否正确哦,同样验证一下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHEtRBdx-1635940847034)(C:\Users\MoWang\AppData\Roaming\Typora\typora-user-images\image-20210824235330709.png)]

4,按位取反:~

同样根据做题来了解计算过程,计算一个~-6的结果

注意在计算机计算的时候,都是采用的补码的方式的,所以按位取反操作同样要得到补码来进行

//按位取反:~ ,取反操作 0变1,1变0
// -6    = 10000000 00000000 00000000 00000110 原码
// -6    = 11111111 11111111 11111111 11111001 反码 最高位不变其他位取反
// -6    = 11111111 11111111 11111111 11111010 补码 反码+1
//result = 00000000 00000000 00000000 00000101 结果补码
//result = 00000000 00000000 00000000 00000101 结果原码 补码为正数原码和补码一致 = 5

这里补充一点⑦Java里面没有无符号数,也就是说Java中所有数都是有符号的。

验证一下计算结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQTMeNcZ-1635940847035)(C:\Users\MoWang\AppData\Roaming\Typora\typora-user-images\image-20210825000214902.png)]

第三组:算数右移:>>,算数左移:<<,逻辑右移:>>>

它们的运算规则分别是

  1. 算数右移:>>,低位溢出,最高位也就是符号位不变,并用符号位的值来补溢出的位置
  2. 算数左移:<<,低位补零,符号位不变
  3. 逻辑右移:>>>,低位溢出,高位补零
5,算数右移:>>

同样用题目来学习,2 >> 2 ,推导过程:

//算数右移:>>,低位溢出,最高位也就是符号位不变,并用符号位的值来补溢出的位置
//   5   = 00000000 00000000 00000000 00000101 最后两位01溢出
//   5   = 00000000 00000000 00000000 00000001 符号位来补溢出的高位
//result = 00000000 00000000 00000000 00000001 正数三码统一,补码等于原码结果为1
System.out.println("5>>2 = "+(5>>2)); 

而>>的本质是5 / 2 / 2 = 1,通过推导我们能更清楚的认识到计算机是怎么帮我们计算的,验证一下推导是否正确:

img

6,算数左移:<<

再出一个题目,这次用上负数,-3 << 2 ,推导过程:

//算数左移:<<,低位补零,符号位不变
//  -3   = 10000000 00000000 00000000 00000011 注意负数要先获得反码,然后通过反码获得补码才能计算
//  -3   = 11111111 11111111 11111111 11111100 -3的反码,符号位不变取反
//  -3   = 11111111 11111111 11111111 11111101 -3的补码,反码+1
//result = 11111111 11111111 11111111 11110100 低位补零,符号位不变
//result = 11111111 11111111 11111111 11110011 结果的反码,补码-1
//result = 10000000 00000000 00000000 00001100 结果的原码,反码取反回来=-12
System.out.println("-3<<2 = "+(-3<<2));

<<的本质是:-322 = -12,可以看到涉及到负数时推导会复杂不少,但是只要一步步来还是很容易理解的,验证一下推导的是否正确:

7,逻辑右移:>>>

现在是最后一个位运算符,注意是没有<<<符号的哦,同样我们来根据做题来推导过程,计算4>>>2的结果:

//逻辑右移:>>>,低位溢出,高位补零 计算 >>>2
//   4   = 000000000 00000000 00000000 0000100 正数原码和补码相同
//result = 000000000 00000000 00000000 0000001 低位溢出,低位溢出,高位补零 = 1
System.out.println("4>>>2 = "+(4>>>2));

img

至此我们就把所有的位运算符过了一遍了,再补充一个知识点,虽然我们没有用到0,但是如果有用到的话**⑧0的反码补码也都是0。**再学习位运算符的过程中,我也把原码、反码、补码的知识点穿插了进来,在用的过程中了解原码、反码、补码的知识。

原码、反码、补码

最后把这些知识点总结一下,得到以下八点:

  1. 二进制的最高位是符号位,0表示整数,1表示负数
  2. 在计算机计算的时候,都是采用的补码的方式
  3. 正数的原码、反码、补码是一样的
  4. 运算结果要看原码
  5. 负数的反码是最高位的符号位不变,其他位取反
  6. 负数的补码 = 它的反码 + 1,反之负数的反码 = 它的补码 - 1
  7. Java里面没有无符号数,也就是说Java中所有数都是有符号的
  8. 0的反码补码也都是0。

这些也是非常重要的知识点,一定要牢牢记下来,俗话说基础不牢地动山摇,看到这一定记得不能光看不练,只有自己走一遍推导的过程才能真正理解位运算的过程,多加练习才能熟练掌握哦。

最后本篇文章知识点主要来自韩顺平老师,经过我自己得梳理和对知识的理解,用自己的话说了出来。

作者(Author):魔王Dany
链接(URL):https://mowangblog.top/mowang/positional-operator
来源(Source):魔王の博客

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值