位运算的学习

位运算的细节补充
#include<bitset>
int main() {
	//因为可能输入的2进制数可能过大导致默认数据类型超过int所以强转int
	int a{ (int)0b100100'01111111111111111111111111111111 };//100100+0+(31个1)
	std::cout << a << std::endl;
	std::cout << std::bitset<70>(a) << std::endl;//可以从a变量地址开始输出70位数据
	return 0;
}//输出如下图示

在这里插入图片描述

解释:首先 a 变量数据在定义时根据二进制强转而来的,只会保留低位32位,然后这32位的最高位作为符号位。因此当我们从 a 变量地址开始输出70个二进制数据时只会有0+(31个1)。小端存储,低位数据低地址。

	int a{ (int)0b100100'11111111111111111111111111111111 };//100100+(32个1)

而此时,更新一下 a 变量的定义后再运行发现输出结果发生了奇妙的变化
在这里插入图片描述

解释:无论如何,当类型转换来定义 a 变量时,都只会保留低32位的数据,即如上的32位的1数据。因此输出 a 变量时最高位符号位为1,按照补码解释数据取反加一得出输出 -1 。奇妙的变化就是不管前面定义时的100100,反转符号位为1时,总会在高位再补32位的1。此时我把变量 a 转成 long long类型去读取时,的的确确在内存变成了64个 1。

左移的边界细节

在最高位的 1 数据左移后没到达符号位时,即为*2

#include<iostream>
#include<bitset>
int main() {
	int a{ (int)0b10111111111111111111111111111111 };//32位数据
	a <<= 1;//a = a << 1;
	std::cout << a << std::endl;
	std::cout << std::bitset<33>(a) << std::endl;
	return 0;
}

在这里插入图片描述

说明:在int类型的32位作用域下,如果左移,高位的1是丢弃,并不会左移到超出作用域。即我上面输出页面特意输出33位数据,其中最高33位是0,说明原来32位的1左移并没有影响到它。然后就是右边补0。【即符号位会被修改】

右移的边界细节

右移为/2,向下取整

#include<iostream>
#include<bitset>
int main() {
	int a{ (int)0b10111111111111111111111111111111 };//32位数据
	a >>= 1;//a = a << 1;
	std::cout << a << std::endl;	
	std::cout << std::bitset<33>(a) << std::endl;
	return 0;
}	

在这里插入图片描述

说明:当a变量的最高32位处符号位为1时,右移则左边恒补1,因为最高位33位为1。【即符号位不会被修改】

int a{ (int)0b01111111111111111111111111111111 };

在这里插入图片描述

说明:当a变量的最高32位处符号位为0时,右移则左边恒补0,因为最高位33位为0。【即符号位不会被修改】

关于左移右移的反汇编情况

调试窗口反汇编可以明显看到图示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

当我们普通运算时的指令和优化后shl,sar的左移右移指令是在汇编不同的。即如果我们*2/4/8会被优化为shl而不是imul,同理算除数时也会被看情况优化成sar右移当除数为2、4、8…

我觉得a*= 3;如果被优化为 a = (a << 1) + a; 可能会更快吧

取反~
与&
或|
异或^

每位对齐逐位运算规则:相同为0,相异为1。

公式①N ^ 0 = 0 ^ N = N ②N ^ N = 0 ③ (a ^ b) ^ c = a ^ (b ^ c) ④ a ^ b ^ c = a ^c ^ b ⑤a ^ b = c; a ^ c = b; c ^ b = a;

位运算的积累运用

TODO:学到就继续补充更新

Ⅰ:交换两个数据而不产生额外空间且效率快。

int a = 5;
int b = 6;
a = a ^ b;//即a = c
b = a ^ b;//即c^b=a,b=c
a = a ^ b;//即c^a=b,a=b

Ⅱ:如支付宝余额5元,在服务端存一个较大的密钥,即每次更新余额时都拿余额和密钥异或得到一个校验值。如果余额异常修改则校验值不对。我这样想的方法是一个保证数据正确性的异或使用

Ⅲ:136. 只出现一次的数字 - 力扣(LeetCode)

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for(int i = 0;i<nums.size();i++){
            res^=nums[i];
        }
        return res;
    }
};

解析:题目得知必定存在有一个数据只出现一次,其他数据必定出现偶次。如样例4,1,2,1,2。

具体代码原理即 res = 0^4^1^2^1^2 = 0^(1^1)^(2^2)^4 = 0^4 = 4。即必定可以移动结合使得相同的数值异或得0,最后只剩下存在一次和0异或。

Ⅳ:260. 只出现一次的数字 III - 力扣(LeetCode)

class Solution {
 public:
  vector<int> singleNumber(vector<int> &nums) {
    unsigned int K = 0;
    for (int i = 0; i < nums.size(); i++) {
      K ^= nums[i];//x^y = K
    }
    unsigned int k = (K & -K);//(K & -K)=={K&(~K+1)}即小写k为大写K的最低位1的值;如K = 1010, k = 0010
    int ans1 = 0;
    int ans2 = 0;
    for (int i = 0; i < nums.size(); i++) {
      if ((nums[i] & k) == 0) {//相同
        ans1 ^= nums[i];
      } else if((nums[i] & k) == k){//不相同
        ans2 ^= nums[i];
      }
    }
    vector<int> ans;
    ans.emplace_back(ans1);
    ans.emplace_back(ans2);
    return ans;
  }
};

解析:同上一题一样,相同值的都会异或碰掉,那么此时只会剩下答案的x,y。对它们两的异或值K分析,即找到最低位的不同的意思是找到最低位的 1 (因为相异为1)【(K & -K)】。此时就可以通过这个特性分两个组

231. 2 的幂 - 力扣(LeetCode)

class Solution {
public:
    bool isPowerOfTwo(int n) {
        if(n<=0) return false;//数据异常特殊处理
        unsigned long long RF1 = (n&-n);
        if( RF1 == n) return true;
        else return false;
    }
};

解析:RF1 = (n&-n)即找到n的最低位的1,如果等于本身即最低位的1唯一一个且也是最高位的1则true。因为2的幂本质就是在二进制只存在一个数据位 1。

class Solution {
public:
    bool isPowerOfFour(int n) {
        if(n <= 0) return false;
        if((n&-n) != n) return false;//先排除不是2的幂
        if((n & 0xaaaaaaaa) == 0) return true;//或者if((n & 0x55555555) == n) return true;
        return false;
    }
};

解析:先通过上题的思路来处理不是2的幂的数据,然后分析4的幂的数据必然是数据位上的1唯一且在奇数位上,那我们取得它得最低位数据1并通过了2的幂的检测后,这个数据位1必定要使得在奇数位则把找出一个所有偶数位为1的数来与之&操作若算出来为0则表示数据1的的确确在奇数位上,反之错误。同理&上所有奇数位1的数据得到本身也是对的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值