位运算(一)

根据昨天的学习情况,询问王某之后,今天应当学习位运算。

异或
and,&ornot,~xor

它们不局限于逻辑运算,均可做用于二进制整数。
在m位二进制中,通常称最低位为第0位,从右到左以此类推,最高位为第m-1位。

补码

32位无符号整数unsigned int:
直接把这32位编码C看做32位二进制数N。
32位有符号整形int:
以最高位为符号位,0表示非负数,1表示负数。
对于最高位为0的每种编码C,直接看作32位二进制数S。
同时,定义该编码按位取反后得到的编码 ~C表示的数值为 -1-S。

32位补码表示unsigned intint
000000…00000000
011111…1111112 147 483 6472 147 483 647
100000…0000002 147 483 647-2 147 483 647
111111…1111114 294 967 295-1

任意两个数值做加减法运算,都等价于在32位补码下做最高位不进位的二进制加减法运算。发生算术溢出时,32位无符号整数相当于自动对232取模。

形式加数加数
32位补码111…111000…001(1)000…000
int-110
unsigned int4 294 967 29510(mod 232)

形式加数加数
32位补码011…111000…001100…000
unsigned int2 147 483 647102 147 483 648
int2 147 483 6471-2 147 483 648

因为用二进制表示一个int需要写出32位,比较繁琐,而用十进制表示,又不容易明显地体现出补码的每一位,所以常用十六进制表示一个常数,书写8个字符,每个字符(0~9 ,A~F)表示补码下的4个二进制位

0x3F 3F 3F 3F
1.整数的两倍不超过0x3F 3F 3F 3F,即int能表示的最大整数。
2.整数的每8位(每个字节)都是相同的。

我们在程序设计中经常使用memset(a,val,sizeof(a))初始化一个int数组a,该语句把数值val(0x00~0xFF)填充到数组a的每个字节上,而1个int占用4个字节,所以用memset只能赋值出“每8位都相同”的int。

移位运算

左移
在二进制表示下把数字同时向左移动,低位以0填充,高位越界后舍弃。
1<<n = 2n, n<<1 = 2n
算术右移
在二进制补码表示下把数字同时向右移动,高位以符号位填充,低位越界后舍弃。
n>>1 = ⌊n/2.0⌋
算术右移等于除以2向下取整,(-3)>> 1 = -2, 3>>1 = 1.
"整数/2"实现为“除以2向零取整”,(-3)/2 = -1, 3/2 = 1.
逻辑右移
在二进制补码表示下把数字同时向右移动,高位以0填充,低位越界后舍弃。

例题:a^b

原题链接

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a,b,p,ans;
	cin >> a >> b >> p;
	ans = 1%p;
	for( ; b ; b >>= 1){
		if(b & 1) ans = (long long)ans * a % p;
		a = (long long)a * a % p;
	} 
	cout << ans;
	return 0;
}

在上面的代码片段中,我们通过“右移(>>)” “与(&)”运算的结合,遍历了b的二进制表示下的每一位。在循环到第i次时(从0开始计数),变量a中存储的是a^2i,若b该位为1,则把此时的变量a累积到答案ans中。
值得提醒的是,在C++语言中,两个数值执行算术运算时,以参与运算的最高数值类型为基准,与保存结果的变量类型无关。换言之,虽然两个32位整数的乘积可能超过int类型的表示范围,但是CPU只会用1个32位寄存器保存结果,造成我们常说的越界现象。因此,我们必须把其中-一个数强制转换成64位整数类型longlong参与运算,从而得到正确的结果。最终对p取模以后,执行赋值操作时,该结果会被隐式转换成int存回ans中。
因为C++内置的最高整数类型是64位,若运算a×bmod p中的三个变量a,b,p 都在1018 级别,则不存在一个可供强制转换的128位整数类型,我们需要一些特殊的处理办法。

例题:64位整数乘法

原题链接

方法一:类似于快速幂

#include<bits/stdc++.h>
using namespace std;
int main()
{
	long long int a,b,p,ans;
	cin >> a >> b >> p;
	ans = 0;
	for( ; b ; b >>= 1){
		if(b & 1) ans = (ans + a) % p;
		a = a * 2 % p;
	} 
	cout << ans;
	return 0;
}

方法二 p6

#include<bits/stdc++.h>
using namespace std;
int main()
{
	unsigned long long a,b,c,x,y,p,ans;
	cin >> a >> b >> p;
	a %= p, b %= p;
	c = (long double)a * b / p;
	x = a * b, y = c * p;
	ans = (long long)(x%p) - (long long)(y%p);
	if(ans < 0)ans += p;
	cout << ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值