移位运算
1. 位运算的常规情况
(1)<< : 左移运算符。
value << n,就是指value的二进制形式整体向左移动n位,表示在十进制上就是value乘以2的n次方。
value << 1就是value乘以2。
1).当value是正数。使用左移运算符移动n位,最左边的n位将会被丢弃,同时在最右边补上n个0。此时的符号位也在移动也被丢弃了(开头的符号位是被补的0替换了),所以一个正数进行左移运算,可能会变成负数。比如下面这样:
为什么会这样?
因为,我们常用的32位编译器或64位编译器下int是都是4个字节(即4x8 = 32位),当使用的是有符号的整型类型时(符号占一位),即有符号的int类型的取值范围是[-2^32 , 2^31 - 1],最大正值的在计算机存储的二值制形式为:0111 1111 1111 1111 1111 1111 1111 1111 ,将其左移一位得1111 1111 1111 1111 1111 1111 1111 1110。注意符号位也移动了(被丢弃了)。
那为啥输出是-2呢? 不是1111 1111 1111 1111 1111 1111 1111 1110吗? 看起来像一个很大的负值。
注意:计算机中使用补码来存储数据(为什么呢?本文后面有资料介绍)。也就是说,1111 1111 1111 1111 1111 1111 1111 1110是个补码。那么我们现在根据补码的原理转为其原码,由于补码的补码即为原码,补码的求法是先取反再加1,将1111 1111 1111 1111 1111 1111 1111 1110取反码(符号位不动,其他位取反)得1000 0000 0000 0000 0000 0000 0000 0001,再加1得 1000 0000 0000 0000 0000 0000 0000 0010,这不就是-2嘛。
2). 当value是负数。使用左移运算符移动n位,最左边的n位将会被丢弃,同时在最右边补上n个0。操作和value为正数时是一样的(一个负数进行左移运算,可能会变成正数),但是请注意,这里操作的是负数,负数在计算机里是以补码的形式进行存储的(其实正数也是,但是正数的补码就是其本身),所以我们进行移位的是负数的补码(符号位也在移动也被丢弃了,即被替换了)。例如:
-2 << 2
-2的原码:1000 0000 0000 0000 0000 0000 0000 0010
-2的反码:1111 1111 1111 1111 1111 1111 1111 1101
-2的补码(反码+1):1111 1111 1111 1111 1111 1111 1111 1110
左移2位: 1111 1111 1111 1111 1111 1111 1111 1000
然后我们将该移位后的补码转为原码就可以知道十进制的-2左移两位后变成多少。
根据补码的补码就是原码进行如下操作:
先求反码:1000 0000 0000 0000 0000 0000 0000 0111
再反码+1: 1000 0000 0000 0000 0000 0000 0000 1000 这就是十进制的-8
也就是-2x4(左移两位) = -8
3). 当value是个无符号的数。无符号数在计算机中直接以对应的二进制表示,没有符号位。直接移就行了,使用左移运算符移动n位,最左边的n位将会被丢弃,同时在最右边补上n个0。
总结:使用左移运算符移动n位,最左边的n位将会被丢弃,同时在最右边补上n个0,符号位也移动了,所以正负性可能会改变。
(2)>> : 右移运算符。
value >> n,即value的二进制数整体向右移n位,表示在十进制上就是value除以2的n次方(向下取整)。
value >> 1就是value 除以2。
右移比左移复杂一些。
1). 当value是个无符号数。使用右移运算符移动n位,最右边的n位将会被丢弃,同时在最左边补上n个0。
2). 当value是个有符号数。使用右移运算符移动n位,最右边的n位将会被丢弃,用该有符号数的符号位填补最左边的n位。也就是说,若value是个正数,则右移n位之后再最左边补n个0;若value是个负数,则右移n位之后再最左边补n个1。注意上面的移位和补0还是补1,操作的都是该有符号数的补码(正数的补码就是其自身,负数的补码是其反码加1)。
例如:
正数右移
10 >> 2
10的原码:0000 0000 0000 0000 0000 0000 0000 1010
10的补码:0000 0000 0000 0000 0000 0000 0000 1010(和原码一样)
右移2位: 0000 0000 0000 0000 0000 0000 0000 0010
即得2
负数右移
-10 >> 2
-10的原码:1000 0000 0000 0000 0000 0000 0000 1010
-10的反码:1111 1111 1111 1111 1111 1111 1111 0101 (符号位不变,其他为取反)
-10的补码:1111 1111 1111 1111 1111 1111 1111 0110 (反码加1)
右移2位: 1111 1111 1111 1111 1111 1111 1111 1101 (最右边2位被丢弃,最左边补2个1)
然后我们将该移位后的补码转为原码就可以知道十进制的-10右移两位后变成多少。
根据补码的补码就是原码进行如下操作:
先求反码:1000 0000 0000 0000 0000 0000 0000 0010
再反码+1: 1000 0000 0000 0000 0000 0000 0000 0011 这就是十进制的-3
也就是-10 >> 2 得-3
咦?10 >> 2 得2,-10 >> 2 得-3,这俩结果绝对值怎么不一样呢?
因为位运算结果向下取整
那么就引出下面一个话题。
2. 除法运算结果向0取整,位运算结果向下取整
如:
负数除二和右移一位的结果不一样
-5 / 2 得-2, -5 >> 1 得-3
但是5 / 2 得2, 5 >> 1 也得2
总结:
除法运算结果都向0取整;位运算结果向下取整
所以对于正数来说位运算和除法结果是一样的,因为正数的向下取整也就是向0取整;而对于负数来说,向下取整要比向0取整小1.
参考博客:https://blog.csdn.net/michaelhan3/article/details/74518925
3. 无符号右移>>>
这个符号C/C++中没有,在java里有
无符号右移>>>(不论正负,高位均补0)
正数:例如4>>>2
与4>>2的运算相同,结果也为1
负数:例如-4>>>2
首先写出-4的二进制数,因为是负数所以最高位为1
-4的原码:1000 0000 0000 0000 0000 0000 0000 0100
然后写出-4补码:保证符号位不变,其余位置取反加1(从右往左遇到第一个1,然后剩下的全部取反就是了)
-4的补码:1111 1111 1111 1111 1111 1111 1111 1100(补码)
右移2位: 在高位补0
0011 1111 1111 1111 1111 1111 1111 1111
结果为:1073741823
无符号右移规则和右移运算是一样的,只是填充时不管左边的数字是正是负都用0来填充,无符号右移运算只针对负数计算,因为对于正数来说这种运算没有意义。
4.与、或、异或的运算
位运算还包括与、或、异或的运算,在这里就不在详细介绍了。注意一点,负数在与、或、异或的运算中也是以补码的形式进行运算的,且符号位参与运算,得的结果是补码的形式,将其转为原码(补码的补码就是原码)我们就知道它是谁了。所以我们可以认为,负数在计算机运算中,都是以补码的形式进行运算,因为负数在内存中就是以补码的形式存储的(其实正数也是以补码的形式存储,正数的补码就是它自身)。
5. 1取反为什么是-2?
1:0000 0000 0000 0000 0000 0000 0000 0001
~1(取反):1111 1111 1111 1111 1111 1111 1111 1110
计算机中存的都是补码:即1111 1111 1111 1111 1111 1111 1111 1110是一个补码。那么我们现在根据补码的原理转为其原码,由于补码的补码即为原码,补码的求法是先取反再加1,将1111 1111 1111 1111 1111 1111 1111 1110取反码(符号位不动,其他位取反)得1000 0000 0000 0000 0000 0000 0000 0001,再加1得 1000 0000 0000 0000 0000 0000 0000 0010,这不就是-2嘛。
6. 如何使用vs在调试时查看内存
可以使用VS查看内存中数据是怎么存的。可以看到负数以补码的形式存储,其实计算机中数据是以补码的形式存储,正数的补码就是自身。
请参考我的这篇博客:https://blog.csdn.net/Young__Fan/article/details/90579437
7. 计算机中使用补码来存储数据
下面介绍原码,反码,补码的知识
正数的原码、反码和补码都是一样的(这是人为规定的),负数的则不同,而且补码的补码为原码
下面的是教材-数字电子技术基础(第五版)(闫石)中的关于补码的详细说明。
参考博客:
右移运算符总结:https://blog.csdn.net/zyh5540/article/details/79217886
C++中左移的使用:https://blog.csdn.net/jzwong/article/details/45022627
移位运算>,>>>:https://blog.csdn.net/java_green_hand0909/article/details/79281774
有符号右移>>,无符号右移>>>:https://blog.csdn.net/bushqiang/article/details/79394211
负数在计算机中如何表示,计算机中负数为什么用补码表示?:https://blog.csdn.net/youcharming/article/details/41982239
相关阅读
问题:如何通过位运算求两个集合的交集、并集、差以及对称差呢?运行截图:其实很简单,步骤如下:①将所有可能出现的元素从1~n编上号这里
题目描述 输入一个正整数N,输出N的阶乘。 输入描述: 正整数N(0<=N<=1000) 输出描述: 输入可能包括多组数据,对于每一组输入数据,输出
向量的叉乘,即求同时垂直两个向量的向量,即c垂直于a,同时c垂直于b(a与c的夹角为90°,b与c的夹角为90°)c = a×b = (a.y*b.z-b.y*a.z ,
代码下载:开根号的几种算法实现在之前的博客中我们介绍了数据类型的地址转换,利用它我们可以将一个float型的值直接看成一个int类型
最近在帮老师制作一些小电路,移相电路也是其中的一种。在制作移相电路的过程中也学习到了不少有用的知识,在这里整理总结一下有关于