Java — 位移、异或运算
什么是二进制数计算?
什么是二进制数
这里就不赘述基础理论了,实在是太多了。从普通人的角度来说就是在计算机中,以01组成的一串数字用来表示普通人能识别的阿拉伯数字、字符等就叫做二进制数。引用百度百科:
二进制是计算技术中广泛采用的一种数制。二进制数据是用0和1两个数码来表示的数。它的基数为2,进位规则是“逢二进一”,借位规则是“借一当二”。 [1]
二进制数(binaries)是逢2进位的进位制,0、1是基本算符 [2] ;计算机运算基础采用二进制。电脑的基础是二进制。在早期设计的常用的进制主要是十进制(因为我们有十个手指,所以十进制是比较合理的选择,用手指可以表示十个数字,0的概念直到很久以后才出现,所以是1-10而不是0-9)。电子计算机出现以后,使用电子管来表示十种状态过于复杂,所以所有的电子计算机中只有两种基本的状态,开和关。也就是说,电子管的两种状态决定了以电子管为基础的电子计算机采用二进制来表示数字和数据。常用的进制还有8进制和16进制,在电脑科学中,经常会用到16进制,而十进制的使用非常少,这是因为16进制和二进制有天然的联系:4个二进制位可以表示从0到15的数字,这刚好是1个16进制位可以表示的数据,也就是说,将二进制转换成16进制只要每4位进行转换就可以了。
二进制的“00101000”直接可以转换成16进制的“28”。字节是电脑中的基本存储单位,根据计算机字长的不同,字具有不同的位数,现代电脑的字长一般是32位的,也就是说,一个字的位数是32。字节是8位的数据单元,一个字节可以表示0-255的十进制数据。对于32位字长的现代电脑,一个字等于4个字节,对于早期的16位的电脑,一个字等于2个字节。
太多了看着头晕是吧?那就好好看看百科怎么解释的。
这里说个重点:二进制和十六进制,八进制一样,都以二的幂来进位的。什么意思?看下面的计算:
5=101=(101)₂=(1x2²)+(0x2¹)+(1x2º)=4+0+1
就是从左往右下标从0开始,然后每一位二进制数乘以2的下标平方然后相加就是十进制数,也就是5阿拉伯数字。如果有小数呢?看下图
为什么要了解二进制数?
这只是计算机基础知识的一小小部分而已。如果你不了解或者学习,那你搞计算机行业真的就失去了很多乐趣。虽然现在各大计算机编程语言已经很成熟了,但是有时候往往最简单的才是最朴实无华而又不失效率的。比如对一两个不同存储地址的数值进行交换(a和b的数值交换),在Java中如果使用最原始的方法就是申请额外的空间进行重新赋值,而使用二进制进行异或只需要几行代码就搞定了,还不需要申请额外的空间存储。是不是很有意思?
位运算
Java中有4个位运算,分别是 “ 按位与 符号为 & ”;“ 按位或 符号为 |”;“ 按位异或 符号为 ^ ”,“按位取反 符号为 ~”,他们的运算规则是:
- 按位与 & —— 两位全为1,结果为1
- 按位或 | —— 两位有一个为1,结果为1
- 按位异或 ^ —— 两位一个为0,一个为1,结果为1;相同为0不同为1
- 按位同或 ^ —— 两位一个为0,一个为1,结果为0,;相同为1不同为零
- 按位取反 ~ —— 正数的补码就是原码也就是本身,先补码再取反0变1,1变0,再转换为原码,最高位符号位不变,取反再+1;负数的补码=最高位符号位不变,取反0变1,1变0,再+1再转换为原码,再取反
位移运算
-
带符号位移
-
不带符号
异或运算
位或运算
位与运算
取反运算
正数取反规律:y= -(x+1);负数取反规律:y = -(-x+1) = [x]-1
补充
异或运算满足交换律结合律。什么意思?
看个问题,一个数组中有一种数出现了奇数次,其他数都出现了偶数次,怎么找到这个数?
int[] arr = {5,5,5,4,4,2,2,3,3,6,6,9,9,9,9};
我们知道,异或的运算规则是:二进制位相同为0不同为1。那么我们先把所有相同的数异或掉,那么偶数的那些数异或的结果都为零。那么只剩下5和0的异或,也就是二进制0000 0000和0000 0101异或,那么结果就是0000 0101,也就是5。
再来一个问题,求一个int型的数值的二进制的最右边的1的二进制数。就是:
int 5 = 00000101;求出 00000001
这里满足一个公式:N & (~N)+1
再来一个问题,redis的bigcount怎么统计一个二进制数上有几个1的呢?假设有一个二进制数:
0000 0101
按照这个思路
- 根据上面的公式求出最右边的1的二进制数出来
- 然后记录得到的最右边的1的数量+1
- 原始二进制数与最右边的1的二进制数进行异或,那么就抵消掉了第一个1
- 然后重复上面的三个步骤