基本概念
^: 按位异或;&:按位与; | :按位或
计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理。
对补码做简单介绍:数值编码分为原码,反码,补码,符号位均为0正1负。
原码 -> 补码: 数值位取反加1
补码 -> 原码: 对该补码的数值位继续 取反加1
补码 的绝对值(称为真值):正数的真值就是本身,负数的真值是各位(包括符号位)取反加1(即变成原码并把符号位取反).
b -> -b : 各位(包括符号位)取反加1
加法运算:将一个整数用二进制表示,其加法运算就是:相异(^)时,本位为1,进位为0;同为1时本位为0,进位为1;同为0时,本位进位均为0.
所以,不计进位的和为sum = a^b,进位就是arr = a&b,(与sum相加时先左移一位,因为这是进位)。完成加法直到进位为0.
由于数据类型所占字节是有限的,而位移的大小却可以任意大小,所以可能存在位移后超过了该数据类型的表示范围,于是有了这样的规定:
如果为int数据类型,且位移位数大于32位,则首先把位移位数对32取模,不然位移超过总位数没意义的。所以4>>32与4>>0是等价的。
如果为long类型,且位移位数大于64位,则首先把位移位数对64取模,若没超过64位则不用对位数取模。
如果为byte、char、short,则会首先将他们扩充到32位,然后的规则就按照int类型来处理。
减法运算:a-b = a+(-b) 根据补码的特性,各位取反加1即可(注意得到的是相反数,不是该数的补码,因为符号位改变了)
(上面用二进制实现的加减法可以直接应用于负数)
应用
- 判断int型变量a是奇数还是偶数
a&1 = 0 偶数
a&1 = 1 奇数 - 求平均值,比如有两个int类型变量x、y,首先要求x+y的和,再除以2,但是有可能x+y的结果会超过int的最大表示范围,所以位运算就派上用场啦。
(x&y)+((x^y)>>1); - 对于一个大于0的整数,判断它是不是2的几次方
((x&(x-1))==0)&&(x!=0); - 比如有两个int类型变量x、y,要求两者数字交换,位运算的实现方法:性能绝对高效
x ^= y;
y ^= x;
x ^= y; - 求绝对值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
} - 取模运算,采用位运算实现:
a % (2^n) 等价于 a & (2^n - 1) - 乘法运算 采用位运算实现
a * (2^n) 等价于 a << n - 除法运算转化成位运算
a / (2^n) 等价于 a>> n - 求相反数
(~x+1)
10 a % 2 等价于 a & 1
等等
当然还有牛人使用位运算来实现权限控制,加密技术。
下面介绍几个常用代码
乘法运算:原理上还是通过加法计算。将b个a相加,注意下面实际的代码。
除法运算:除法运算是乘法的逆。看a最多能减去多少个b,
#include
include
using namespace std;
//递归版本的加法实现
int Add(int a, int b)
{
return b ? Add(a^b, (a&b)<<1) : a;
/*
if(b)
return plus_rec(a^b, (a&b)<<1);
else
return a;
*/
}
//该为迭代版本
int Add_iter(int a, int b)
{
int ans;
while(b)
{
ans = a^b;
b = (a&b)<<1;
a = ans;
}
return ans;
}
//求a的相反数:将各位取反加一
int negative(int a) //get -a
{
return Add(~a, 1);
}
int Minus(int a, int b)
{
return Add(a, negative(b));
}
//正数乘法
int Multi(int a, int b)
{
int ans = 0;
while(b)
{
if(b&1)
ans = Add(ans, a);
a = a << 1;
b = b >> 1;
}
return ans;
}
//正数除法
int Divide(int a, int b)
{
int coun = 0;
while(a >= b)
{
a = Minus(a, b);
coun = Add(coun, 1);
}
return coun;
}
//判断是否是负数,0,正数
int isneg(int a)
{
return a & 0x8000;
}
int iszero(int a)
{
return !(a & 0xFFFF);
}
int ispos(int a)
{
return (a&0xFFFF) && !(a&0x8000);
}
//处理负数的乘法和除法
int My_Multi(int a, int b)
{
if(iszero(a) || iszero(b))
return 0;
if(isneg(a))
{
if(isneg(b))
return Multi(negative(a), negative(b));
else
return negative(Multi(negative(a), b));
}else if(isneg(b))
return negative(Multi(a, negative(b)));
else
return Multi(a, b);
}
int My_Divide(int a, int b)
{
if(iszero(b))
{
cout << “Error!” << endl;
exit(1);
}
if(iszero(a))
return 0;
if(isneg(a))
{
if(isneg(b))
return Divide(negative(a), negative(b));
else
return negative(Divide(negative(a), b));
}else if(isneg(b))
return negative(Divide(a, negative(b)));
else
return Divide(a, b);
}
int main(int argc, char **argv)
{
int a = 5;
int aa = -5;
int b = 3;
int bb = -3;
int c = 15;
cout << Add(a, b) << endl;
cout << Add(a, bb) << endl;
cout << Minus(a, b) << endl;
cout << Minus(b, a) << endl;
cout << Multi(a, b) << endl;
cout << My_Multi(aa, b) << endl;
cout << Divide(c, a) << endl;
return 0;
}