今天跟大家分享的小技巧是关于位移运算的。大多数数的编程语言都采用“>>”和“<<”运算符来对变量做位移运算,也就是我们通常所说的“右移”和“左移”。顾名思义,右移运算表示将一个数的每一位都向右移动一位,而左移则表示将一个数的每一位都向左移动一位。
一、无符号数的位移运算:
0001 1010 右移一位(>>1) 0000 1101
26 13
0000 1101 右移一位(>>1) 0000 0110
13 6
0000 0110 右移一位(>>1) 0000 0011
6 3
0000 0011 右移一位(>>1) 0000 001
2 1
0000 0001 右移一位(>>1) 0000 0000
1 0
很显然右移运算的结果等于原来数值的1/2,这里有一个细节需要注意,对于无符号数的右位移运算最高位是补0的,所以在经过了有限次右移运算后,此无符号数则会变为0。
我们再来看看左移运算的结果:
0001 1010 左移一位(<<1) 0011 0100
26 52
0011 0100 左移一位(<<1) 0110 1000
52 104
0110 1000 左移一位(<<1) 1101 0000
104 208
1101 0000 左移一位(<<1) 1010 0000
208 160
1010 0000 左移一位(<<1) 0100 0000
160 64
0100 0000 左移一位(<<1) 1000 0000
64 128
1000 0000 左移一位(<<1) 0000 0000
128 0
通常对无符号数左移运算的结果等于这个数的2倍,但也不是绝对的,我们看到当208向右位移一位之后结果为160,这是为什么呢?原因很简单,在对无符号数向左位移运算时最低位补0,但高位则会溢出,也就是说1101 0000 (208)左移一位 原来最高位的1溢出了,于是结果就变成了1010 0000(160)左移的结果非但不是原来的2倍,反而变小了,所以我们在做位移运算时一定要注意变量的类型,在内存中的存储大小,一定要考虑到出现高位溢出的情况。
二、有符号数的位移运算:
先来看看对一个负数做右位移运算:
1001 1100 右移一位(>>1) 1100 1110
-100 -50
1100 1110 右移一位(>>1) 1110 0111
-50 -25
1110 0111 右移一位(>>1) 1111 0011
-25 -13
1111 0011 右移一位(>>1) 1111 1001
-13 -7
1111 1001 右移一位(>>1) 1111 1100
-7 -4
1111 1100 右移一位(>>1) 1111 1110
-4 -2
1111 1110 右移一位(>>1) 1111 1111
-2 -1
1111 1111 右移一位(>>1) 1111 1111
-1 -1
在上面对负数的位移运算中,我们可以看到负数的位移运行跟正数不同,每右移一位结果是原来数值的2倍,并且为了保证负数的符号不变所以高位补1,当-2右移一位后结果为-1,而-1的补码为1111 1111,接下来我们再对-1进行一次右位移运算,结果仍然是-1,因为高位仍然补1,所有的bit位均为1。所以我们在对负数做位移做运算时一定要考虑到负数的符号位。
我们再来看看对负数的左位移运算:
1001 1100 左移一位(<<1) 0011 1000
-100 56
0011 1000 左移一位(<<1) 0111 0000
56 112
0111 0000 左移一位(<<1) 1110 0000
112 -32
1110 0000 左移一位(<<1) 1100 0000
-32 -64
1100 0000 左移一位(<<1) 1000 0000
-64 -128
1000 0000 左移一位(<<1) 0000 0000
-128 0
0000 0000 左移一位(<<1) 0000 0000
0 0
有两个地方我们需要注意一下:
第一,高位溢出后计算机并不保留原符号位的符号,而只把位移后符号位的值做为新的符号。也就是说:1001 1100 (-100)左移一位0011 1000(56)原来最高位是1,表示这是一个负数,值为-100,但经过了一次左位移运算之后,最高位变成了0,于是这个数则变成了一个正数,值为56;接下来0111 0000 (112)左移一位 1110 0000(-32)原来最高位是0,表示这是一个正数,值为112,但经过了一次左位移运算之后,最高位变成了1,于是这个数则变成了一个负数,值为-32。
第二,在对有符号数的左位移运算时,最低位补0。所以经过有限次的左位移运算之后,结果将会是0。
Copyright © 2015-2023 问渠网 辽ICP备15013245号