【笔记】位运算

/ 0评 / 0

本文记录位运算的常见应用。

在介绍位运算的应用之前,应当首先对二进制表示有一定的了解,这方面知识属于计算机原理的范畴。我们知道,按位与、按位或、按位异或、按位取反就是对整数的二进制位逐个进行逻辑与、逻辑或、逻辑异或、逻辑取反来得到的。所以我们首先要考虑的是,整数是怎么通过二进制表示的?

我们知道常用的二进制表示整数的方法有原码、反码、补码三种,这三种方法在正整数上的表示是相同的,就是按数学上的二进制数位来直接表示,区别只在于零和负整数上。

在英文中,原码称为sign/magnitude,而反码称为ones' complement,有意思的是,ones' complement 的中文直译是“一的补码”,而国内所说的补码用英文称为two' complement,中文直译是“二的补码”。

三种方法的区别在于,已知某正整数的二进制表示,得到其相反数的二进制表示的方法是不同的:

一个16位表示5和-5的例子如下:

numbertwo's complementones' complementsign/magnitude
50000 0000 0000 01010000 0000 0000 01010000 0000 0000 0101
-51111 1111 1111 10111111 1111 1111 10101000 0000 0000 0101
三种表示方法示例

既然负数有这三种表示法,那么对于一个负数进行位操作的结果就与平台有关了。在C语言标准里,规定了必须使用上面三种方法之一,但并没有强制要求使用哪一种方法,幸运的是,现在绝大多数机器所使用的都是补码(two' complement)。JAVA在各类数据类型下的二进制表示则遵循下面的规则:

byte8-bittwo's complement
short16-bittwo's complement
int32-bittwo's complement
long64-bittwo's complement
float32-bitIEEE 754
double64-bitIEEE 754
boolean由VM决定由VM决定
char16-bitUnicode character
JAVA TYPES

还要注意到:整数的二进制表示与整数在内存中储存的实际数据在顺序上并不总是一致,这主要是由于不同机器下字节序的不同。主要有两种方案:大端方案(Big-Endian)和小端方案(Little-Endian)。

所谓大端方案,即高位字节储存在内存低位地址上,低位字节储存在内存高位地址上,直观地说,即内存中储存的字节序与二进制表示的顺序一致。如 0x12345678采用大端方案,在内存中按地址从低到高储存为[12 34 56 78]。而小端方案则正好相反,若采用小端方案,在内存中则储存为[78 56 34 12]。

注意,无论是大端方案还是小端方案,它们规定的只是字节顺序,而不是位顺序。假设使用补码,整数-5在两种方案下的储存如下:

two's complement1111 1111 1111 1011
Big-Endian1111 1111 1111 1011
Little-Endian1111 1011 1111 1111
两种方案示例

大小端方案的选用是由机器决定的,目前绝大多数的个人电脑都使用小端方案。在进行位运算时,由于运算结果也是用同一方案储存的,所以无论是哪种方案,都不会影响位运算的结果,程序员依旧只需从二进制表示(补码)的角度去理解,无需关注内存中的实际细节。这个的前提是程序代码不涉及内存上字节层次的操作,例如在汇编语言中,可能需要取两个字节的某些位进行拼接,这个时候字节序就无法忽略了。

至此,在对整数在内存中的实际情况有一定了解后,我们便可以应用位运算了,并知晓这些位运算在负数上操作时所可能产生的结果。下面介绍几个常用的操作:

使用上面这些操作,便可以自由对某个字节上的任意位进行操作了,这引出了位运算的一个最常见的应用:位开关变量:即使用一个字节上的8个位来表示8个只有两种状态的变量,通过上述位运算来对其中每个位进行检测和修改,节省了空间。

接下来介绍一个有趣的应用,利用异或找出出现奇数次的数。

给定一个整数数组,其中有唯一元素出现了奇数次,其余元素都出现了偶数次,试找出那个出现了奇数次的整数。由于一个数与自身的异或为0,且异或具有交换律,所以对该数组中所有元素进行异或,重复偶数次的元素都会被消除(变为0),异或的结果即是出现奇数次的那个元素。时间复杂度为O(n),空间复杂度为O(1)。这个例子说明异或可以用于去重。

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *