本文首发于公众号「 MoTec 」,阅读原文效果更佳。 >>> 传送门

我们知道,目前的计算机最终只认识 0 和 1 这两个数字,我们写的所有代码、指令最终都会变成以 0 和 1 组成的编码执行的,而这样的编码就叫做二进制。

至于为什么是 0 和 1 呢?我简单、非官方地解释一下,因为计算机是由无数个逻辑电路组成的,而电路的逻辑只有 0 和 1 两个状态,0 和 1 并不是简单数字意义上的 0 和 1,它们表示两种不同的状态,0 表示低电平,1 表示高电平。要控制电路来表达某种意思,就只能控制不同电路的不同状态即根据 0 和 1 的有限位数和组合来表达。

因此像我这些从事计算机相关学习或者工作的人就自诩「我的世界里只有 0 和 1」。

而今天我要说的「位运算」,就是直接对这些二进制位进行的一些操作,当然也只是在数值方面。常用的二进制位操作有,~(取反)、^(异或)、>>、<<、&(与)、|(或),在 Java 中还有 >>>,下面是一些简单的规则

另外补充一下,1 & X = X,0 & X = 0,1 | X = 1,0 | X = X

最重要的是,在计算机系统中,数值一律用补码来表示(存储)。 因为使用补码可以将符号位和其它位统一处理,同时,减法也可按加法来处理。

其中,如果是我们要人为计算的话(一些面试题,很恶心),碰到负数一定要一万个小心,负数在内存中存储的是它的补码,而它是原码取反加 1 而不是像正数那样,补码和原码一样,另外取反操作也需要特别小心。

~(取反)

0 和 1 全部取反,0 变为 1,1 变为 0。即 ~ 0 = 1,~ 1 = 0。一定要特别要注意的是,这里的 0 和 1 是二进制位中的,它是一个位,跟我们常用的十进制中的 0 和 1 区别非常大!举个例子,顺便说一下正数的取反运算,你或许会清楚怎么回事。你觉得下面的代码会输出什么?

class Test {public static void main(String[] args) {System.out.println(~1);}
}

会是 0 吗?大错特错!千万别以为这是前面说的 ~ 1 = 0,答案是 -2

为什么是 -2 呢?代码里的 1 跟前面规则表格中的 1 区别很大,表格中的 1 是具体到某一位,真正的位操作,而代码里 1 是十进制中的 1,它是 int 类型,在 Java 中,它要用 4 个字节即 32 位来表示,即

那它取反怎么成 -2 了呢?首先它是正数,它的补码和原码是一样的,也就是
00000000 00000000 00000000 00000001
特别提醒的是,上面的是 1 的补码,取反之后是
11111111 11111111 11111111 11111110

注意最高位,也就是我们说的符号位,它也会被取反,0 变 1,竟成了负数!同时一定要知道它是补码,要转换成原码的话,先 -1 再取反,因为负数的补码是由原码取反后再 +1,现在是逆过程。

注意在这次取反过程中,符号位是不用取反的,但前面 ~ 取反操作是要取反的,这也是我们很容易错的地方。

再来看看负数 -5 的 ~ 取反操作

class Test {public static void main(String[] args) {System.out.println(~-5);}
}

你可以先动手试试,看看结果是不是 4


为了方便,我这里就不再以 32 位来做演示,而是只用 8 位,后面有些例子也是如此

小结,取反操作是不管符号位的,总之都取反,0 变 1,1 变 0,而在原码和补码间转换时,虽然也有个取反过程,但是符号位是不变的,这也是我们经常会混淆的,是坑。

另外,对所有位操作,实际上都是对它的补码操作,这个适用于任何位操作。对于正数,巧就巧在补码和原码一样,而负数的补码是原码的取反加 1,所以我们也会混淆。

| 与、& 或

对于 ^ 异或运算我在这里就不多说了。就说说我在 | 或运算的小结,大家也可以类推到 & 与运算,然后,再说说 Java 中 >> 和 >>> ,就结束。码字好累,原创不易,多多点赞支持,谢谢。

先给个小题,16 | 15 = ?
我先不直接揭晓,一起来看看计算过程,还是以 8 位来做演示

最终结果是 31,不知大家有没有觉得蹊跷,16 | 15 = 31 = 16 + 15,在这里,| 或运算相当于加法运算。

其实还可以看看其他例子,32 | 9 = 41 = 32 + 9

为什么会这样呢?因为 1 | X = 1,0 | X = X ,我们再认真看位运算的过程


我们以第一个加数为基数,末尾除了右起第 6 位,都是 0 ,而第二个加数又小于它,一经过 | 或运算, 0 | X = X ,其实也是将两位数加一起。

所以这里有个小结论,2^N 与一个小于它的数做 | 或运算,其实就是它们两个数之和。知道这个结论,我们以后做题时运算效率就更高一些。就像数字转 IP 的算法就把这个用到极致,它还结合 << 。

public long ipToLong(String ipStr) {long result = 0;String[] ipAddressInArray = ipStr.split("\\.");for (int i = 3; i >= 0; i--) {long ip = Long.parseLong(ipAddressInArray[3 - i]);// 等同 A * 256^3 + B * 256^2 + C * 256^1 + D * 256^0,运用位移、或 位运算更高效result |= ip << (i * 8);}return result;}

更深层次的,在非负两数或运算中,只要两数换成二进制数时,对应的位不是 1 | 1,或运算结果都与加法运算结果一致,我称它为或运算中的非双一现象

上面的代码就可以很好的诠释,其中 0b 表示二进制数的写法,就好像 0x 表示十六进制一样道理,数值我是随便给的,不是我故意,大家回去可以试试。通常我们会感觉没什么卵用,还不如前面的小结论来得实在点,其实不然,如果知道这些现象且用得非常 6,在加密、算法效率方面用处是非常大的,我就因为欠缺这个而丢失一份很好的工作。

位移

先解释符号及运算规则,>>,带符号右移,正数右移高位补 0,负数右移高位补 1;

4 >> 1 = 2


-4 >> 1 = -2

无符号右移。无论是正数还是负数,高位通通补 0 。

4 >>> 1 = 2

-4 >>> 1 = 2147483646

代码运行结果验证

小结一下,对于正数而言,>> 和 >>> 没区别。对于负数,>> 将二进制高位用 1 补上,而 >>> 将二进制高位用 0 补上,区别就很大。

另外,位运算可以帮我们高效地完成很多事情,例如求平均数、判断奇偶、不借助第三方交换两个数 ……,简单了解后,我的世界观都重造了,计算机的世界里好神奇,有兴趣可以查阅相关博客和书籍。

位运算总结,我的世界里只有 0 和 1相关推荐

  1. 47 - 算法 - 记住常用位运算-Leetcode-136-只出现一次的数字

    // 位运算 相同的 a^a = 0 a^0 = a 其它 a&a = a a&0 = 0 class Solution {public:int singleNumber(vector ...

  2. Java位运算优化:位域、位图棋盘等

    快速小测试:如何重写下面的语句?要求不使用条件判断语句交换两个常量的值. if (x == a) x= b; else x= a; 答案: x= a ^ b ^ x; //此处变量x等于a或者等于b ...

  3. 【简单】191. 位1的个数(汉明重量)常规+位运算解法(所谓简单可它真的简单吗?)

    [题目] 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量). 来源:leetcode 链接:https://leetcode-cn.com/pro ...

  4. ios 按位运算---分解ZFPlayer

    按位与&(位运算时,相同为1,不同为0) 按位或 |(位运算时,有1为1,全0为0) 按位异或 ^(位运算时,相同为0,不同为1) 按位取反~(0变1,1变0) 左移<  把整数a的各二 ...

  5. SQL位运算查询 - ‘‘ 举例

    需求举例: 某动物园对动物的投喂有以下规定:      苹果可以去投喂鹿.猴子.熊猫      竹子可以去投喂熊猫.竹鼠      树叶可以去投喂兔子.鹿 现在饲养员携带苹果.树叶,他可以投喂哪些动物 ...

  6. 进制转换 位运算(包括补码、原码、反码、~0等一些零碎东西一次说清)

    我发现网上关于标题上的内容介绍的都很零碎,因此为了方便查找.也为了本人对这一部分的充分理解,就想着写一篇这样的博客(我分成了几个部分,以便查找): 一.进制转换 让我们先来看看各个进制的定义: 十进制 ...

  7. 剑指Offer - 面试题53 - II. 0~n-1中缺失的数字(二分、位运算)

    文章目录 1. 题目 2. 解题 2.1 充分利用题目信息 2.2 位运算 2.3 二分查找 1. 题目 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内.在范 ...

  8. Leetcode 第1342题:将数字变成 0 的操作次数 (位运算解题法详解)

    前言 Leetcode第1342题如果用直观方式来做,其实是一道难度极低的题目.但是如果采用位运算的方式来解,则会涉及许多有趣的衍生知识点,了解其背后的原理对我们认识位运算有很大的帮助.现在,就让我们 ...

  9. usaco Section 1.5 Checker Challenge 最慢0.162秒0.0+n皇后问题位运算版(C语言)

    今天做USACO做到Section 1.5的Checker Challenge 直接dfs之后的结果是,超时.. 百度查查才想起来就是八皇后问题.有人讲怎么利用对称性怎么怎么优化,我没仔细看 直到看到 ...

最新文章

  1. 计算机视觉基本原理——RANSAC
  2. 谷歌AI一次注释了10%的已知蛋白质序列,超过人类十年研究成果
  3. Extjs--FormPanel(2)
  4. 用express、mongodb、nodejs开发简单的登陆
  5. 网站转移服务器,网站转移云服务器
  6. 数据源改成mysql_flowable流程war修改成数据源为mysql
  7. Python入门--类的创建
  8. OpenDDS项目练习指南
  9. python 英语分词_Python实现中英文分词
  10. 服务器sel信息是什么意思,英特尔?服务器主板 — 如何解压和读取的服务器事件日志(SEL)...
  11. 光环五正在连接服务器,五亿美元打水漂了?追了这么久的光环系列,玩家根本没法看...
  12. ASP.NET MVC5中局部视图的更新
  13. three.js使用外部模型创建动画,使用GLTF格式文件动画创建动画(vue中使用three.js71)
  14. 关于Java,你想知道的一切,这里说透了
  15. 杭电1856——并差集
  16. Alphat【翻译】
  17. php 接口token生成验证
  18. UnrealBuildTool的RulesScope-Unreal4源码拆解-UnrealBuildTool功能流程解析
  19. 如何从正确的原理图生成PCB图
  20. [AHK]输入法状态提示,中文状态提示“中”,英文状态提示“EN”

热门文章

  1. 一般腻子(普通腻子)和耐水腻子的区别
  2. LTE-TDD measurement gaps位置计算--Python代码实现
  3. uni-app 全端通用组件库unify_Ui(全端兼容nvue和vue),所有组件全端兼容,可配置全局样样式,350+常用图标
  4. ArcGIS中的土地利用变化分析详解
  5. 用应用宝安装自己打包的apk的时候,提示包损坏
  6. html5+css3基础内容
  7. 网络空间态势感知 序
  8. 今天公司下午放半天假,下午干什么好呢?在公司加班?去外面浪?回家睡觉?还是?
  9. PMP到底难不难考?
  10. 攻防世界Misc入门题之坚持60s