位运算

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

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值