二进制究竟有什么用?带你看看那些好玩儿的「位操作」

文章来源于公众号码农田小齐 ,作者小齐本齐

计算机说到底就是 0 和 1,所有的数在内存中都是以二进制的形式储存的。

而位操作,或者说位运算,就是直接对内存中的二进制位进行操作。

位运算可以说是我们的基本功,今天这篇文章就从以下角度和大家一起玩转位运算。

  1. 位运算究竟有什么用?
  2. 原码 反码 补码
  3. 7 种位运算

当然了,位运算还有很多奇技淫巧,如果大家还想看进阶篇,记得给我点赞或者留言告诉我哦~

位运算的作用

在实际生产中,位运算是用来优化时间和空间的。

位运算可能并不会降低复杂度的等级,但是可以把复杂度前面的系数降下来。

举个例子。

大家都知道堆,或者叫优先队列,一般来说是用完全二叉树来实现的,叫做二叉堆

image

<figcaption style="margin: 5px 0px 0px; padding: 0px; max-width: 100%; text-align: center; color: rgb(136, 136, 136); font-size: 14px; box-sizing: border-box !important; word-wrap: break-word !important; overflow-wrap: break-word !important;">最小堆</figcaption>

二叉堆插入、删除元素的时间复杂度都是 O(logn),如果这个不清楚的同学赶紧在公众号内回复「」复习一下,或者点击这里~

但是有另一种堆,它能够做到 O(1) 的时间插入元素,O(logn) 的时间删除元素,我在堆这篇文章里也提到过,就是斐波那契堆

但为什么不用呢?

就是因为 O(1) 前面的系数非常大。

image

我们说 O(logn)O(1) 好,是有个条件的,那就是 n 非常非常大的情况下,但是实际上,如果 n 是在 int 范围内,那么取个 log 也不过就是 32 了,反而这个 O(1) 的时间复杂度可能系数达到几百几千。

一般来说实际应用中时间的测量并不是像我们做算法题时用时间复杂度这么简单,有的时候就需要你把两个算法都实现出来,去跑去测量它的时间,才能决定哪个好。

那么二进制一次能够作用于 32 位上(假设是一个 int),如果数据表示的巧妙,这完全可以优化 32 倍,多用几个 int 就多优化了好几个 32 倍,不香吗?

image

除了优化时间,还可以优化空间。

比如在网站发布新版本时,一般都会附上支持该版本的浏览器列表,不然有些老掉牙的浏览器看不到我的新功能还算我的锅么?

那么怎么有效的表示这个浏览器列表呢?

全世界所有浏览器都有个国际标准编号,这里我就简单假设一下:

  • 0 表示 QQ 浏览器
  • 1 表示 Chrome 浏览器
  • 2 表示火狐浏览器
  • 3 表示 ...

那么我们就可以用一个 int 表示是否支持这些浏览器的状态,如果这个浏览器能用,那么这一位上就设为 1,不能用就设为 0,这样用一个 int 就能表示 32 个网站。

那么国内的某个网站可以表示为:

  • 0b .... 1101

所以位操作在很多代码里都很常用,比如网络协议、操作系统等等。

这就是位运算的两大优势,接下来我们说说具体的知识点。

原码 反码 补码

数字有正有负,Java 中用的是 signed type,就是有正有负的。

虽然在 Java 8 之后,也用了个工具来实现 unsigned type,但是其实底层实现是没有的。

二进制最左边的一位是符号位,

  • 0 表示这个数是非负数;
  • 1 表示这个数是负数。

对了,最左边的一位英文叫做 most significant bit,别说错了。。。

正数

正数的原码反码补码相同,没啥好说的。

比如:

  • int 1 = 0b 0000 0000 0000 0001
  • int 2 = 0b 0000 0000 0000 0010

负数:

原码:把相应的正数的符号位设为 1。

  • -1 的原码 = 0b 1000 0000 0000 0001
  • -2 的原码 = 0b 1000 0000 0000 0010

反码 ones' complement

符号位是 1,其余位取反。

  • -1 的反码 = 0b 1111 1111 1111 1110
  • -2 的反码 = 0b 1111 1111 1111 1101

补码 two's complement

反码 + 1。

  • -1 的补码 = 0b 1111 1111 1111 1111
  • -2 的补码 = 0b 1111 1111 1111 1110

而计算机中真正用来存储数据的是用补码

这里稍微注意下反码和补码的英文,ones' 的这个 ' 在后面,two's 的这个 ' 在中间。。

为什么计算机要用补码来存储数据呢?

可能有同学会说正零负零的原因,但这只是表面现象。

实际上通过补码这样精巧的设计,计算机做加减乘除运算就不用考虑符号,就可以让硬件里 CPU 的设计变得异常简单。

最初计算机只有加法器没有减法器,所以它用这么一种方式用加法完成了减法。

int 的最大值是多少?

正是因为最左边一位是符号位,所以正数的表示就少了一位能用的,那么 int 的最大值就是:

0111111...11 (31 ones) = 2^31 - 1 = 2147483647

7 种位运算

运算符 中文 英文 运算规则
<< 左移 left shift 右边补充 0
>> 右移 signed right shift 左边补充符号位
>>> 无符号右移 unsighed right shift Java 特有,左边补充 0
~ 位非 NOT 每位取反
& 位与 bitwise AND 每位做与操作,都是 1 则为 1,否则为 0
I 位或 OR 每一位做或操作,有 1 则为 1,否则为 0
^ 异或 XOR 相同为 0,不同为 1

要注意的是前 4 个运算符是对 1 个数进行操作的,且操作完成后这个数本身的值不变(与 i++ 不同);后 3 个操作是两个数的运算。

我们一一来看。

为了书写方便,下面的数值虽然是 int 类型,但我只写 8 位,大家都能理解的噢!

1. <<

左移操作就是把这些零啊壹啊的整体往左移动 n 位,右边缺的就补充 0。

  • 1 = 0b 0000 0001

  • 1 << 1 = 0b 0000 0010 = 2

  • 2 = 0b 0000 0010

  • 2 << 1 = 0b 0000 0100 = 4

  • 3 = 0b 0000 0011

  • 3 << 1 = 0b 0000 0110 = 6

诶,大家发现没有,左移 1 位之后这个数相当于乘2

但是这只适用于左边溢出的高位中不包含 1 时。

如果把 1 扔了,那就肯定不是 2 倍了嘛。

image

2. >>

右移操作就是整体往右移动 n 位,左边缺的补充符号位。

  • 1 = 0b 0000 0001

  • 1 >> 1 = 0b 0000 0000 = 0

  • 2 = 0b 0000 0010

  • 2 >> 1 = 0b 0000 0001 = 1

  • 3 = 0b 0000 0011

  • 3 >> 1 = 0b 0000 0001 = 1

同理,正数右移操作的效果是这个数除以 2

如果是负数呢?

  • -3 = 1111 1101
  • -3 >> 1 = 1111 1110 = -2

因为 Java 是向零取整,所以奇数时会有问题,就不再是除以 2 的结果。

总结一下,

  • 对于非负数、负数且是偶数,右移一位与除以 2 结果一样;
  • 对于负数且是奇数,右移一位不等于除以2。

3. >>>

和 >> 的不同之处在于,这个的左边空缺的不论正负,一律补充 0。

所以对于正数来说,和 >> 的效果一样,但是负数不同。

  • -3 = 1111 1101
  • -3 >> 1 = 01111 1110 = 很大的数。。

4. ~

取反操作,就是每一位取反,1变成0,0变成1。

  • 3 = 0b 0000 0011
  • ~3 = 0b 1111 1100

5. &

这个符号其实和逻辑与运算 && 意思一样,只不过作用在每一位上。

对于每一位来说,两个数都是真,则为真,否则为假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3&5 = 0b 0000 0001

6. |

同理,和逻辑或运算 || 意思一样,只不过作用在每一位上。

对于每一位来说,但凡有个真的就是真,否则为假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3|5 = 0b 0000 0111

7. ^

最后一个异或操作,相同为 0,不同为 1。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3^5 = 0b 0000 0110

好啦,以上就是位运算的基本操作了,由这些运算做些巧妙的组合还可以得到很多有趣的结果,大家喜欢的话记得点赞评论哦~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354