位运算详解
xsobi 2024-11-23 10:50 1 浏览
1.原文地址
http://www.lgygg.wang/lgyblog/2019/11/11/%e4%bd%8d%e8%bf%90%e7%ae%97/
2.位移符号
>>相当于使当前二进制对应的10进制数除以2.
<<相当于使当前二进制对应的10进制的数乘以2.
例如,a=2,b=9;
a的二进制数是:0010,如果a左移1位,即a>>1,得到0001,即十进制数1。
b的二进制数是:1001,如果b右移1位,即b<<1,得到10010,即18。
所以,如果要求两个数的平均值,可以使用(a+b)>>1,得到0101,即十进制数5,这里你会疑问,(2+9)/2=5.5,这里就是说左移只能对结果是偶数的数值求平均值才能得到正确结果,对应奇数结果的平均值,他会向下取整,所有得到5.5。
3.~求负数
某个正整数的负数可以通过~取反后+1来获得。
假设a=1,求它的负数,即-1的二进制表示。
1的二进制表示是,0001,
-1就是正整数二进制的反码加1,即(~1)+1,(1110)+0001=1111,
这里只是使用4位二进制来表示,当然如果使用8位二进制表示来计算,可以表示为
1的8位二进制表示,00000001,
-1的8位二进制表示,11111111
4.&判断奇偶
&可以用来判断10进制数的奇偶。
例如,判断a=10,b=23是奇数还是偶数。
a的二进制数是00001010
b的二进制数是00010111
只需要和1进行与操作即可,因为一个奇数的二进制表示中1的个数肯定是偶数,而一个偶数的的二进制表示中1的个数肯定是奇数个。为零为偶,否则奇数。
同a&1和b&1来判断a和b是奇数还是偶数。
00001010
&
00000001
结果是,00000000,所有a是偶数。
00010111
&
00000001
结果是,00000001,所有b是奇数。
5.^判断两个数是否相等
只需要对两个数进行异或运算后,如果结果为0则证明相等,否则不相等。
假设a=3,b=4,c=3,判断a==b,a==c是否成立。
a的二进制,0011
b的二进制,0100
c的二进制,0011
0011
^
0100
结果为,0111,结果不为零,所以a!=b
0011
^
0011
结果为,0000,结果为零,所以a==c
6.^交换两个数值
假设现在有两个数值a=4,b=9,交换这两个数,只需要执行
a^=b
b^=a
a^=b
计算过程:
已知a=0100,b=1001
0100
^
1001
结果:a=1101,b=1001
1001
^
1101
结果:b=0100,a=1101
1101
^
0100
结果:a=1001,b=0100
7.位运算实现加减乘除
加法运算
使用位运算实现加法运算主要分为两个部分。
假设需要对a,b两个值进行加法运算,
步骤一,先计算完全不考虑进位进行相加的值,通过异或操作来实现。a = a^b,a即是不考虑进位相加后的值。
步骤二,计算只考虑进位的产生值,通过(a&b)<<1可以得到只考虑进位产生的值,将这个值赋值给b,即b=((a&b)<<1)。如果计算出来的b值不等于0,就说明发生了进位,就需要重复步骤一和步骤二,直到b的值为0。
例子:假设a=12,b=19,通过位运算来实现a+b。
a的二进制数:00001100
b的二进制数:00010011
先执行异或操作,算出a+b不考虑进位的情况下,得到的值,什么叫不考虑进位的值?通过10进制的计算来说明不考虑进位的计算,或许能让人更容易理解。
还是a=13,b=19,计算a+b,
13
+
19
可以看到,个位上3+9=12,是发生了进位的,正常情况下(也就是考虑进位的情况下),需要向十位进1,得到的结果就是32,但是,在不考虑进位的情况下,得到的结果就是22,很明显,这个22并不是正确的计算结果,我们需要将这个22加上刚才3+9产生的进位值进行相加22+10,才是正确的值。那么回到二进制的计算分析过程,
00001101
^
00010011
结果是a =00011110,这是二进制进行不考虑进位情况下得出的结果。
再计算他们相加的过程有没有发生进位,通过(a&b)<<1
00001101
&
00010011
进行与运算得出的结果是00000001,然后向左移动1位,得到结果是
b=00000010,可以看到这个结果不为0,所以它是发生了进位。
所以需要重复步骤一和步骤二。下面就不再赘述,只是展示重复步骤的过程
第二次
00011110
^
00000010
a=00011100
b=00000100
第三次
00011100
^
00000100
a=00011000
b=00001000
第四次
00011000
^
00001000
a=00010000
b=00010000
第五次
00010000
^
00010000
a=00000000
b=00100000
第六次
00000000
^
00100000
a=00100000
b=00000000
这时候b等于0了,而a=00100000,也就是十进制的32,所以结果是正确的。
C++代码如下:
//方法一:使用循环
int bit_add(int a, int b)
{
int add, carry; //carry代表进位
int i = 1;
do {
add = a ^ b;
carry = (a & b) << 1;
a = add;
b = carry;
cout << "步骤" << i++ << ":" << add << "\n";
} while (carry != 0);
return add;
}
C++
输出结果:
减法运算
实现a – b只要实现a + (-b)即可。所以只要将a和b的相反数调用add函数就行。根据二进制数在机器中表达的规则,得到一个数的相反数,就是这个数的二进制数表达取反加1(补码)的结果。
int bit_subtract(int a, int b)
{
return bit_add(a, bit_add(~b, 1));
}
C++
下面的例子是计算13-19
结果:
乘法运算
用位运算实现乘法运算。a × b的结果可以写成 a?2^0?b_0+a?2^1?b_1+…a?2^i?b_i+…a?2^31?b_31. 其中b_i为0或1表示整数b的二进制表达中第i位的值(从右往左数)。该过程有点类似于求整数的N次方问题。具体实现参照如下代码:
这里只考虑了正整数相乘的结果。
int multi(int a, int b)
{
int res = 0;
while(b != 0)
{
if(b & 1 == 1)
//这里的add是二进制的加法运算函数
res = add(res, a);
a <<= 1;
b >>= 1;
}
return res;
}
C++
假设a=12,b=9,计算a*b。
a的二进制数:00001100
b的二进制数:00001001
第一遍:通过b&1判断最后一位是不是1,很明显b的最后一位是1,所以这个位上是会产生值得,所以
b&1的值为,00000001
res =00001100
a=00011000
b=00000100
第二遍:
b&1的值为,00000000,因为等于零,所以没有产生数值,所以res不变
res =00001100
a=00110000
b=00000010
第三遍:
b&1的值为,00000000,因为等于零,所以没有产生数值,所以res不变
res =00001100
a=01100000
b=00000001
第四遍:
b&1的值为,00000001,
res =01101100
a=11000000
b=00000000
这里b=0,计算结束,所以最好a*b的值为01101100,即十进制数108。
除法运算
用位运算实现除法运算其实就是乘法的逆运算。定义 res 表示除法的结果。首先将a向右移位31位,然后看能不能容下b,如果能,说明a/2^31可以包含一个b,等价于a可以包含一个b?2^31,令res的第31位为1,此时a的值应该为a?b?2^31;如果不能容下b,令res的第31位为0,a的值不变;接下来将a向右移位30位是否能容下b……重复步骤直到a?b?2^i=0。
以上过程只适用于a和b都不是负数的情况下,当a或b为负数时,可以先将a和b转成正数,计算完之后再判断res的真实符号就行。
除法实现到这一步已经可以解决绝大多数情况了。但是我们知道,32位最小整数的绝对值要比最大整数大,所以如果a或b等于最小值,是不能转换成相对应的正数的。这时候需要分情况考虑:
如果a和b都为最小值,直接返回1
如果a不为最小值,而b为最小值,那么a/b = 0,直接返回0
如果a为最小值,而b不为最小值。这时我们对a无能为力,但是我们可以让a增大一点点,计算出一个结果然后再修正一下就可以得到最终的结果。处理过程如下:
<1>计算(a+1)/b,结果记为c
<2>计算c?b
<3>计算(a?c?b)/b,结果记为rest
<4>计算c+rest
代码实现如下:
代码中的INT_MIN在标准头文件limits.h中定义。
#define INT_MAX 2147483647
#define INT_MIN (-INT_MAX - 1)
在C/C++语言中,不能够直接使用-2147483648来代替最小负数,因为这不是一个数字,而是一个表达式。表达式的意思是对整数21473648取负,但是2147483648已经溢出了int的上限,所以定义为(-INT_MAX -1)。
C中int类型是32位的,范围是-2147483648到2147483647 。
(1)最轻微的上溢是INT_MAX + 1 :结果是 INT_MIN;
(2)最严重的上溢是INT_MAX + INT_MAX :结果是-2;
(3)最轻微的下溢是INT_MIN - 1:结果是是INT_MAX;
(4)最严重的下溢是INT_MIN + INT_MIN:结果是0
int divide(int a, int b)
{
if(b == 0)
cout<<"Input Error!"<<endl;
else if(a == INT_MIN && b == INT_MIN)
return 1;
else if(b == INT_MIN)
return 0;
else if(a == INT_MIN)
{
int c = div(a+1, b);
return add(c, div(minus1(a, multi(c, b)), b));
}
else
return div(a, b);
}
int div(int a, int b)
{
a = a >= 0? a : add(~a, 1);
b = b >= 0? b : add(~b, 1);
int res = 0;
for(int i=31; i>-1; i=minus1(i, 1))
{
if((a>>i) >= b)
{
res = res | (1<<i);
a = minus1(a, b << i);
}
}
return a >= 0 && b >= 0? add(~res, 1) : res;
}
C++
8.总结
位运算只能对整数进行操作。
9.参考文章
https://blog.csdn.net/sodacoco/article/details/79629208
https://www.jianshu.com/p/5d974bb015cf
https://blog.csdn.net/sodacoco/article/details/79629208
- 上一篇:Java学习入门技术点:位移运算符
- 下一篇:年近半百自学Python之位运算
相关推荐
- js向对象中添加元素(对象,数组) js对象里面添加元素
-
一、添加一个元素对象名["属性名"]=值(值:可以是一个值,可以是一个对象,也可以是一个数组)这样添加进去的元素,就是一个值或对象或数组...
- JS小技巧,如何去重对象数组?(一)
-
大家好,关于数组对象去重的业务场景,想必大家都遇到过类似的需求吧,这对这样的需求你是怎么做的呢。下面我就先和大家分享下如果是基于对象的1个属性是怎么去重实现的。方法一:使用.filter()和....
- 「C/C++」之数组、vector对象和array对象的比较
-
数组学习过C语言的,对数组应该都不会陌生,于是这里就不再对数组进行展开介绍。模板类vector模板类vector类似于string,也是一种动态数组。能够在运行阶段设置vector对象的长度,可以在末...
- 如何用sessionStorage保存对象和数组
-
背景:在工作中,我将[{},{}]对象数组形式,存储到sessionStorage,然后ta变成了我看不懂的形式,然后我想取之用之,发现不可能了~记录这次深刻的教训。$clickCouponIndex...
- JavaScript Array 对象 javascript的array对象
-
Array对象Array对象用于在变量中存储多个值:varcars=["Saab","Volvo","BMW"];第一个数组元素的索引值为0,第二个索引值为1,以此类推。更多有...
- JavaScript中的数组Array(对象) js array数组
-
1:数组Array:-数组也是一个对象-数组也是用来存储数据的-和object不同,数组中可以存储一组有序的数据,-数组中存储的数据我们称其为元素(element)-数组中的每一个元素都有一...
- 数组和对象方法&数组去重 数组去重的5种方法前端
-
列举一下JavaScript数组和对象有哪些原生方法?数组:arr.concat(arr1,arr2,arrn);--合并两个或多个数组。此方法不会修改原有数组,而是返回一个新数组...
- C++ 类如何定义对象数组?初始化数组?linux C++第43讲
-
对象数组学过C语言的读者对数组的概念应该很熟悉了。数组的元素可以是int类型的变量,例如int...
- ElasticSearch第六篇:复合数据类型-数组,对象
-
在ElasticSearch中,使用JSON结构来存储数据,一个Key/Value对是JSON的一个字段,而Value可以是基础数据类型,也可以是数组,文档(也叫对象),或文档数组,因此,每个JSON...
- 第58条:区分数组对象和类数组对象
-
示例设想有两个不同类的API。第一个是位向量:有序的位集合varbits=newBitVector;bits.enable(4);bits.enable([1,3,8,17]);b...
- 八皇后问题解法(Common Lisp实现)
-
如何才能在一张国际象棋的棋盘上摆上八个皇后而不致使她们互相威胁呢?这个著名的问题可以方便地通过一种树搜索方法来解决。首先,我们需要写一个函数来判断棋盘上的两个皇后是否互相威协。在国际象棋中,皇后可以沿...
- visual lisp修改颜色的模板函数 怎么更改visual studio的配色
-
(defunBF-yansemokuai(tuyuanyanse/ss)...
- 用中望CAD加载LISP程序技巧 中望cad2015怎么加载燕秀
-
1、首先请加载lisp程序,加载方法如下:在菜单栏选择工具——加载应用程序——添加,选择lisp程序然后加载,然后选择添加到启动组。2、然后是添加自定义栏以及图标,方法如下(以...
- 图的深度优先搜索和广度优先搜索(Common Lisp实现)
-
为了便于描述,本文中的图指的是下图所示的无向图。搜索指:搜索从S到F的一条路径。若存在,则以表的形式返回路径;若不存在,则返回nil。...
- 两个有助于理解Common Lisp宏的例子
-
在Lisp中,函数和数据具有相同的形式。这是Lisp语言的一个重大特色。一个Lisp函数可以分析另一个Lisp函数;甚至可以和另一个Lisp函数组成一个整体,并加以利用。Lisp的宏,是实现上述特色的...
- 一周热门
- 最近发表
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- patch补丁 (31)
- strcat (25)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- element style (30)
- dedecms模版 (53)
- vs打不开 (29)
- nmap (30)
- webgl开发 (24)
- parse (24)
- c 视频教程下载 (33)
- android 开发环境 (24)
- paddleocr (28)
- listview排序 (33)
- firebug 使用 (31)
- transactionmanager (30)
- characterencodingfilter (33)
- getmonth (34)
- commandtimeout (30)
- hibernate教程 (31)
- label换行 (33)