根据昨天的学习情况,询问王某之后,今天应当学习位运算。
与 | 或 | 非 | 异或 |
---|---|---|---|
and,& | or | not,~ | 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 int | int |
---|---|---|
000000…000000 | 0 | 0 |
011111…111111 | 2 147 483 647 | 2 147 483 647 |
100000…000000 | 2 147 483 647 | -2 147 483 647 |
111111…111111 | 4 294 967 295 | -1 |
任意两个数值做加减法运算,都等价于在32位补码下做最高位不进位的二进制加减法运算。发生算术溢出时,32位无符号整数相当于自动对232取模。
形式 | 加数 | 加数 | 和 |
---|---|---|---|
32位补码 | 111…111 | 000…001 | (1)000…000 |
int | -1 | 1 | 0 |
unsigned int | 4 294 967 295 | 1 | 0(mod 232) |
。
形式 | 加数 | 加数 | 和 |
---|---|---|---|
32位补码 | 011…111 | 000…001 | 100…000 |
unsigned int | 2 147 483 647 | 1 | 02 147 483 648 |
int | 2 147 483 647 | 1 | -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;
}