最近遇到一个跟位运算相关的代码,想想开发这么多年来,还是上学那会在课堂上使用过,在实际的项目中从未使用过,就造成了前辈代码看不懂的困境,今天就来切身学习分享一下
看到不少网友说,大公司面试可能会经常出一些跟位运算相关的面试题,比如:
腾讯有一个:
如何检测整数 n 是否是 2 的幂次和?
在看一道Google面试题:
有64瓶药,其中63瓶是无毒的,一瓶是有毒的。如果做实验的小白鼠喝了有毒的药,3天后会死掉,当然喝了其它的药,包括同时喝几种就没事。现在只剩下3天时间,请问最少需要多少只小白鼠才能试出那瓶药有毒?
位 指的是比特位(bit),不是byte,所以位运算指的就是比特位计算。
CPU所有计算都是二进制的计算,一个高性能的服务一定是把CPU资源利用到极致,也就是用最少资源换取最大收益。
在计算机世界里,万物皆0、1,0、1生万物。万物到0、1的过程叫做编码。
一个数在计算机中的二进制表示形式, 叫做这个数的机器数。机器数是带符号的,在计算机中用一个数的最高位存放符号, 正数为0, 负数为1。
计算机中对数字的编码表示有三种方式:原码,反码,补码:
原码:原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1。比如十进制3如果用8个二进制位来表示就是 00000011, -3就是 10000011。
反码:反码表示方法:正数的反码是其本身;负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
补码:补码表示方法:正数的补码是其本身;负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1。 (即在反码的基础上+1)
这三种是编码方式,但是在计算机系统中,数值一律用补码来表示(存储)。
举个例子来说明下这三种码
1. 15 原码 反码 补码 00001111 --> 00001111 --> 00001111 2. -15 10001111 --> 11110000 --> 11110001
各种编程语言都提供了对补码的二进制位直接进行运算的方法,即位运算。
符号 | 描述 | 规则 |
---|---|---|
& | 与 | 相同位的两个数字都为1,则为1;若有一个不为1,则为0。 |
| | 或 | 相同位只要一个为1即为1。 |
~ | 非 | 0和1全部取反。 |
^ | 亦或 | 相同位不同则为1,相同则为0。 |
<< | 左移 | a << b就表示把a转为二进制后左移b位(在后面添b个0)。 |
>> | 右移 | a >> b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。 |
接下来举几个例子来说明下:
“&”运算
“|”运算
“>>”运算
“<<”运算
位运算优势
- 存储更友好,比特位存储,不用转换后在存储
- CPU更友好,直接比特位操作,减少机器数到比特位的转换
- 寻址次数更少,左移一位就乘2
那么我来解析下前辈的代码(也就与开篇提到的腾讯面试题一个原理了):