位运算及其应用

基本概念

^: 按位异或;&:按位与; | :按位或

计算机系统中,数值一律用补码来表示:因为补码可以使符号位和数值位统一处理,同时可以使减法按照加法来处理。

对补码做简单介绍:数值编码分为原码,反码,补码,符号位均为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即可(注意得到的是相反数,不是该数的补码,因为符号位改变了)

(上面用二进制实现的加减法可以直接应用于负数)

应用

  1. 判断int型变量a是奇数还是偶数
    a&1 = 0 偶数
    a&1 = 1 奇数
  2. 求平均值,比如有两个int类型变量x、y,首先要求x+y的和,再除以2,但是有可能x+y的结果会超过int的最大表示范围,所以位运算就派上用场啦。
    (x&y)+((x^y)>>1);
  3. 对于一个大于0的整数,判断它是不是2的几次方
    ((x&(x-1))==0)&&(x!=0);
  4. 比如有两个int类型变量x、y,要求两者数字交换,位运算的实现方法:性能绝对高效
    x ^= y;
    y ^= x;
    x ^= y;
  5. 求绝对值
    int abs( int x )
    {
    int y ;
    y = x >> 31 ;
    return (x^y)-y ; //or: (x+y)^y
    }
  6. 取模运算,采用位运算实现:
    a % (2^n) 等价于 a & (2^n - 1)
  7. 乘法运算 采用位运算实现
    a * (2^n) 等价于 a << n
  8. 除法运算转化成位运算
    a / (2^n) 等价于 a>> n
  9. 求相反数
    (~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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值