位运算那些事~

计算机中数值全部用补码表示

正数: 源码,反码,补码均相同

负数: 补码 = 反码 + 1

-1 源码: 1000 0000 0000 0000 0000 0000 0000 0001

-1 反码: 1111 1111 1111 1111 1111 1111 1111 1110

-1补码: 反码+1 = 1111 1111 1111 1111 1111 1111 1111 1111 

-1用二进制补码表示为32个1

为什么呢? 

由于int 在32位和64位机器中均占用4个字节(1字节=8bit) ,所以-1的二进制补码为32个1


<1> 进制的转换

(1)十进制转二进制:


6->110 ;


(2)二进制转十进制

110 = 1*2^2  +  1*2^1 +  0*1^0    =  6 ;


(3)十进制小数转二进制

6.8125 转二进制

整数部分如上述(1)所示  6 -> 110   ,

小数部分:

0.8125 * 2 = 1.625 取整数  1  余小数部分0.625

0.625 * 2 = 1.25 取整数  1 余小数部分0.25

0.25 * 2 = 0.5 取整数 0 余小数0.5

0.5 * 2 = 1.0 取整数1 余小数0.0

结束

小数部分为1101

因此

6.8125  转二进制为  110.1101


(4) 二进制小数转十进制

110.1101转十进制 :

110.1101 =  1*2^2  +  1*2^1 +  0*1^0   .   1*2^-1  + 1*2^-2  +  0*2^-3  + 1*2^-4


其它进制转换不再陈述


<2>  位运算

按位与          a & b           

按位或         a | b

按位异或      a ^ b

按位取反      ~a

左移              a << b

右移              a >> b


(3) 位运算基本规律:

1101  & 1011  = 1001

1101  |   1011 = 1111 

~1101 = 0010

①移位操作

左移时,正负都一样右边加0

正数: 0000 1101 << 1  =  0001 1010

负数: 1000 1101 << 1  =  0001 1010

右移时,正数左边加0,负数左边加1

正数: 0000 1101 >> 1  =  0000 0110

负数: 1000 1101 >> 1  =  1100 0110


注:

右移相当于除2

左移相当于乘2


②异或

1101  ^ 1011 = 0110

应用不用第三个参数交换a b

a = a^b;

b = a^b;

a = a^b;


③与

去掉一个数字的二进制的最后一个1

n = n&(n-1) ;


获取一个数字的二进制的最后一个1

n = n&~(n-1) ; 

n = n&(-n) ; 


检测第i位是否为1

bit = n & ( 1 <<  i );


 清除第i位(即置为0)

n = n & ~( 1 << i );


第i位置为1

n = n | ( 1 << i );


第i位置取反

n = n ^ ( 1 << i );



位运算实例:

<pre name="code" class="cpp">#include <iostream>
using namespace std;

/*
  题目:任意给定一个32位无符号整数n,求n的二进制表示中1的个数,
  比如n = 5(0101)时,返回2,n = 15(1111)时,返回4
*/
//右移法:当输入负数时,出现死循环
int NumberOf1(int n)
{
	int count = 0;
	while (n)
	{
		if (n & 1)
		{
			count++;
		}
		n=n >> 1;   //右移相当于除以2
	}
	return count;
}
//左移法: 循环次数等于整数二进制的位数,即32位的整数需要循环32次
int NumberOf1t(int n)
{
	int count = 0;
	unsigned int flag = 1;
	while (flag)
	{
		if (n&flag)
		{
			count++;
		}
		//flag = flag * 2;等价于flag = flag << 1;但位运算速度更快
		flag = flag << 1;
	}
	return count;
}

//把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变为0.
//那么一个整数的二进制表示中有多少个1,就可以进行多少次这样的操作.
int NumberOf1tt(int n)
{
	int  count = 0;
	while (n)
	{
		count++;
		n = (n - 1)&n;
		//cout << n << endl;
	}
	return count;
}

//n左移法:打印整数的二进制数
void IntToBit(int n)
{
	int flag = 1;
	while (flag)
	{
		if (n & flag)
		{
			cout << 1;
		}
		else
		{
			cout << 0;
		}
		flag = flag << 1;
	}
	cout << endl;
}

//递归实现,从第一位开始输出,即递归之前输出
void IntToBit2(int n, int flag)
{
	if (flag == 0)
	{
		return ;
	}
	
	if (n & flag)
	{
		cout << 1;
	}
	else
	{
		cout << 0;
	}

    IntToBit2(n, flag<<1);
}

//递归实现,从最后一位开始输出,即先递归到二进制的最高位,再输出
void IntToBit3(int n, int flag)
{
	if (flag == 0)
	{
		return ;
	}
	
    IntToBit3(n, flag<<1);
	
	if (n & flag)
	{
		cout << 1;
	}
	else
	{
		cout << 0;
	}
}


/*题目47: 不用加减乘除做加法*/
int Add(int num1, int num2)
{
	//sum记录相加的值,carry记录进位
	int sum, carry;
	do
	{
		//各位相加不进位
		sum = num1 ^ num2;
		//做进位
		carry = (num1&num2) << 1;

		num1 = sum;
		num2 = carry;
	} while (num2 != 0);

	return num1;
}

//求数值的相反数,即反码+1
int  Opposite(int num)
{
	//反码+1
	return Add(~num, 1);
}
//二进制减法
int  Subtract(int num1, int num2)
{
	return Add(num1, Opposite(num2));
}

//二进制乘法
int Multiply(int num1, int num2)
{
	//判断两个数相乘之后的正负号(异或:一正一负返回true,两正或两副返回false)
	bool isNeg = (num1 > 0) ^ (num2 > 0);

	//判断两个数的正负,并统一转化为正数
	int x = num1 > 0 ? num1 : Opposite(num1);
	int y = num2 > 0 ? num2 : Opposite(num2);

	int ans = 0;
	while (y != 0)
	{
		if (y & 1)
		{
			ans = Add(ans, x);
		}
		y=y >> 1;
		x=x << 1;
	}
	
	return isNeg ? Opposite(ans) : ans;
}
//查找最小值
int Min(int a, int b)
{
	return a < b ? a : b;
}

//将被除数的j位之前的所有位都置为0(由于我们这里都已经转换为正数,所以不用考虑符号位)
int DeleteHigeBite(int num,int j)
{
	for (int i = j; i <= 31; i++)
	{
		num = num&~(1 << i);
	}
	return num;
}

//二进制除法
int Divide(int num1, int num2)
{
	//判断两个数相乘之后的正负号(异或:一正一负返回true,两正或两副返回false)
	bool isNeg = (num1 > 0) ^ (num2 > 0);

	//判断两个数的正负,并统一转化为正数
	int x = (num1 > 0) ? num1 : Opposite(num1);
	int y = (num2 > 0) ? num2 : Opposite(num2);

	int ans = 0;

	for (int i = 31; i >= 0; i--)
	{
		//让x右移i位找到被除数x可以被除数除尽的点
		//即模拟除法运算:被除数x从左侧开始被除
		if ((x >> i) >= y)
		{
			//去掉i位之前的所有位都(即置为0),让x从剩下的被除数开始
			//(由于我们这里都已经转换为正数,所以不用考虑符号位)
			x = DeleteHigeBite(x, i);

			ans = Add(ans, 1 << i); //商
		}
	}

	return isNeg ? Opposite(ans) : ans;
}



int main(void)
{
	cout << NumberOf1(7) << endl;
	/*
	  计算机中数值均用补码表示
	  正数的原码反码补码全相同
	  负数的反码=原码取反,补码=反码+1.
	*/
	cout << NumberOf1t(-1) << endl; //32
	cout << NumberOf1tt(-7) << endl; //32

	//打印整数5的二进制,
	//由于左移相当于乘2,右移相当于除2,则说明二进制右侧为最低位
	IntToBit(5); 
	IntToBit2(5,1);
	cout << endl;
	IntToBit3(5, 1);
	cout << endl;

	/*题目47: 不用加减乘除做加法*/
	cout << Add(5, 17)<<endl;  // +  22
	cout << Subtract(5,17) << endl; //- -12
	cout << Multiply(-5, -10) << endl;  //85
	cout << Divide(-14,3) << endl;  //取整除法 -4

	return 0;
}



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值