c/c++中位运算总结

一、无符号数
即没有符号的数。 在c语言中就是 unsigned 类型的。无符号数在计算机中的存储较为简单, 因为没有符号位, 直接将数字化成二进制然后存储在对应的存储器或者寄存器中。 这时寄存器或者存储器的位数就可以表示数值的范围
二、有符号数
即有符号的数, 符号数存储时不仅要将数据部分存入计算机, 还要将符号数字化的存进去。表示方法有:
原码,反码,补码
所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。

反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
在计算机中,数据是以补码的形式存储的。
三、位运算
除非必要,否则不要对有符号数进行位运算!!!
1、与(&)运算
(1)运算法则

两个二进制数进行与&运算,如果对应位都为1则结果为1,否则为0.

(2)技巧及用途

与运算常常用于二进制下的取位操作。想要知道二进制下的某位是否是1,就&上这个位数对应的十进制数。假如返回的是这个十进制数本身,则这个位的确是1,反之就是0.

比如:

我们要取第三位是否为1,我们只需要与&上第三位(二进制表示为100)对应的二进制数4,如果返回值为4,就代表第三位为1,反之就是0.

最常用的是取二进制下的最末位,即a&1。这样的技巧可以用于判断奇偶,根据二进制常识,尾数为1则为奇数,反之为偶数。
2、或(|)运算
(1)运算法则

两个二进制数进行或|运算,如果对应位有一个为1,结果就为1.只有在两个数的对应位置都是0的时候,结果才为0.

(2)技巧及用途

或运算常用于二进制特定位的赋值。想把哪个位强行变成1,就用这个数|上这个位数对应的二进制数。

还是上面那个例子,我们想让00000的第三位变成1.即十进制变4,我们直接|上4就可以。

当然,不同于&运算,我们很少用|运算进行任意位赋值。通常来讲,我们只使用a|1把a的最后一位强行变成1,其实质意义是把原数加一。或者使用a|1-1再把它变为0.这个技巧通常用于把它变成它最接近的偶数。
2、或(|)运算
(1)运算法则
两个二进制数进行或|运算,如果对应位有一个为1,结果就为1.只有在两个数的对应位置都是0的时候,结果才为0.

(2)技巧及用途
或运算常用于二进制特定位的赋值。想把哪个位强行变成1,就用这个数|上这个位数对应的二进制数。

还是上面那个例子,我们想让00000的第三位变成1.即十进制变4,我们直接|上4就可以。

当然,不同于&运算,我们很少用|运算进行任意位赋值。通常来讲,我们只使用a|1把a的最后一位强行变成1,其实质意义是把原数加一。或者使用a|1-1再把它变为0.这个技巧通常用于把它变成它最接近的偶数。
3、异或(^)(xor)运算
(1)运算法则
两个二进制数进行异或(^)运算,如果对应位相同,不管是0或者是1,都返回0,反之返回1.

(2)技巧及用途
一个数经过两次异或之后等于原数。
4、非(~)运算
(1)运算法则
把给定二进制数全部取反。

(2)技巧及用途

5、左移(<<)运算
(1)运算法则

a<<b表示把a的二进制位向左移动b位,低位用0补上。

(2)技巧及用途

根据二进制的常识,我们会发现,二进制第k位上的数就等于2^k。(从0开始计位)

所以我们发现,左移运算a<<b的实质就是a×2^b。

左移运算最常用的技巧就是用来代替×2的整数次幂的乘法运算。因为我们普遍认为,位运算是要比四则运算加减乘除及模运算更快一些的运算。

6、右移(>>)运算
(1)运算法则
右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位或者一律补0,或者补符号位,这由不同的机器而定。
a>>b就是把a的二进制位向右移动b位,移出的舍去。

(2)技巧及用途

类比于左移运算,我们发现右移运算就是把a除以2的整数次幂。这就是右移运算的用途——优化除法运算。

这里需要特殊说明的是,右移算法可以用在数学知识中的求最大公约数的程序块上。因为mod运算的效率慢的出奇,所以我们可以用右移运算来进行除以2的操作。

逻辑移位:只是逻辑上的移位操作,移位后的数据不符合对原数据进行任何算术运算得出的结果。
算术移位:对原数据移位后的结果符合某种运算规则,如对无符号数向左移n位,在不溢出的情况下相当于乘以2^n。
有符号数:
左移:如果数为正,则符号位为0,在不溢出的情况下,且移动的第一个非零位为到达符号位时,左移一位相当于乘2;如果移动的第一个非零位到达符号位,则数的符号将改变,不再遵守算术规则。如果数为负,则符号位为1,左移会把符号位移掉,故不遵守算术规则。
右移:如果数为正,不溢出的情况下,每移一位相当于除以2。如果数为负,移一位相当于除以2,不会出现溢出情况。
无符号数:
左移:不溢出的情况下,左移一位相当于乘2。
右移:不溢出的情况下,右移一位相当于除2。

7、位运算优先级
位运算的优先级是我们在处理位运算的时候常常要考虑的问题,诚然,我们可以用括号强制位运算的顺序,但是,我们还是应该学会位运算的优先级(这应该是常识)。

位运算的优先级如下:

按位反(~)>位移运算(<<,>>)>按位与(&)>按位异或(^)>按位或(|)

附:位运算在状压DP的用法
众所周知,状压DP就是把状态压缩成一个01串(其实就是一个二进制数),用以减少DP数组的维数。但是我们在DP的时候就要按照01串来进行状态的转移。所以位运算是状压DP的基础知识和必备知识。所以我在本篇随笔的末尾还附上了状压DP中比较常用的操作及其二进制实现的方式。

正文:(本文中的a表示十进制下的整数)

1、获得第i位的数字:(a>>i)&1 或者 a&(1<<i)

很好理解,我们知道可以用&1来提取最后一位的数,那么我们现在要提取第i位数,就直接把第i位数变成最后一位即可(直接右移)。或者,我们可以直接&上1左移i位,也能达到我们的目的。

2、设置第i位为1:a=a|(1<<i)

我们知道强制赋值用|运算,所以就直接强制|上第i位即可。

3、设置第i位为0:a=a&(~(1<<i))

这里比较难以理解。其实很简单,我们知道非~运算是按位取反,(1<<i)非一下就变成了第i为是0,其它全是1的二进制串。这样再一与原数进行&运算,原数的第i位无论是什么都会变成0,而其他位不会改变(实在不明白的可以用纸笔进行推演)。

4、把第i位取反:a=a^(1<<i)

1左移i位之后再进行异或,我们就会发现,如果原数第i位是0,一异或就变成1,否则变成0。

5、取出一个数的最后一个1:a&(-a)

学过树状数组的同学会发现,这就是树状数组的lowbit。事实上,这和树状数组的原理是一样的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值