移位的位數(shù)是負(fù)數(shù),結(jié)果會(huì)怎樣?
有過(guò)編程經(jīng)驗(yàn)的同學(xué),對(duì)于移位操作應(yīng)該很熟悉了,日常工作中或多或少都有用到,當(dāng) 移位位數(shù)是負(fù)數(shù) 或者 移位位數(shù)超過(guò)了 類型的最大二進(jìn)制位時(shí),和正常移位處理是不一樣的,下面將詳細(xì)說(shuō)明這兩種情況,在此之前,先了解下正常的移位操作
正數(shù)的左移和右移
正數(shù)的左移是二進(jìn)制位向左移動(dòng),右邊留空的位置補(bǔ) 0,右移是二進(jìn)制位向右移動(dòng),左邊留空的位置補(bǔ) 0 ( 符號(hào)位為 0 )
- 左移
左移操作,最高位的符號(hào)位會(huì)出現(xiàn) 0 或 1 , 因此結(jié)果會(huì)出現(xiàn)正數(shù)和負(fù)數(shù)的情況
新建測(cè)試文件 base.cpp,代碼如下
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t a = 7;
- cout << "(a << 2) = " << (a << 2) << endl;
- cout << "(a << 30) = " << (a << 30) << endl;
- return 0;
- }
執(zhí)行 g++ -g -Wall -std=c++11 -o base base.cpp 命令編譯代碼,再執(zhí)行 ./base 運(yùn)行程序,結(jié)果如下
- (a << 2) = 28
- (a << 30) = -1073741824
上述代碼中,變量 a 的值為 7,對(duì)應(yīng)的二進(jìn)制是 0000 0000 0000 0000 0000 0000 0000 0111
左移 2 位:二進(jìn)制向左移動(dòng) 2 位,右邊補(bǔ)充 2 位 0 ,左邊丟棄超出的 2 位二進(jìn)制, 結(jié)果是 0000 0000 0000 0000 0000 0000 0001 1100, 對(duì)應(yīng)的十進(jìn)制數(shù)是 28
左移 30 位 的流程如下圖
由上圖可知,二進(jìn)制向左移動(dòng) 30 位后, 左邊 30 位二進(jìn)制 0000 0000 0000 0000 0000 0000 0000 01 因超出被丟棄,同時(shí)最右邊剩下的 2 位二進(jìn)制 11 左移 30 位,右邊空的位置補(bǔ)充 30 位 0,最終的結(jié)果是 1100 0000 0000 0000 0000 0000 0000 0000, 對(duì)應(yīng)的十進(jìn)制數(shù)是 -1073741824
可以看出,正數(shù) 7 左移 30 位后,二進(jìn)制的符號(hào)位變成了 1 ,也即正數(shù)變成了負(fù)數(shù)了
- 右移
正數(shù)右移,最小為 0 , 不會(huì)出現(xiàn)負(fù)數(shù),下面是右移的測(cè)試代碼
修改 base.cpp 文件,代碼如下
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t a = 7;
- cout << "(a >> 1) = " << (a >> 1) << endl;
- cout << "(a >> 31) = " << (a >> 31) << endl;
- return 0;
- }
編譯并運(yùn)行上面程序,結(jié)果如下:
- (a >> 1) = 3
- (a >> 31) = 0
上述實(shí)例代碼中,變量 a 的值為 7,對(duì)應(yīng)的二進(jìn)制是 0000 0000 0000 0000 0000 0000 0000 0111
右移 1 位:二進(jìn)制向右移動(dòng) 1 位,左邊補(bǔ)充一位 0 ,右邊丟棄超出的一位二進(jìn)制, 結(jié)果是 0000 0000 0000 0000 0000 0000 0000 0011, 對(duì)應(yīng)的十進(jìn)制數(shù)是 3
右移 31 位:二進(jìn)制向右移動(dòng) 31 位,左邊補(bǔ)充 31 位 0 ,右邊丟棄超出的 31 位二進(jìn)制,結(jié)果是 0000 0000 0000 0000 0000 0000 0000 0000, 對(duì)應(yīng)的十進(jìn)制是 0
負(fù)數(shù)的左移和右移
負(fù)數(shù)的左移是二進(jìn)制位向左移動(dòng),右邊留空的位置補(bǔ) 0,右移是二進(jìn)制位向右移動(dòng),左邊留空的位置補(bǔ) 1 ( 符號(hào)位為 1 ),這一點(diǎn)跟正數(shù)是不一樣的
計(jì)算機(jī)中是用補(bǔ)碼的形式進(jìn)行各種運(yùn)算的,正數(shù)的補(bǔ)碼是其自身,負(fù)數(shù)的補(bǔ)碼是將其正數(shù)按位取反加 1
- 左移
負(fù)數(shù)左移,符號(hào)位可能會(huì)變成0,因此結(jié)果會(huì)出現(xiàn)正數(shù)和負(fù)數(shù)的情況,一直左移的話,最終會(huì)變成 0
修改 base.cpp文件,代碼如下
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t b = -3;
- cout << "(b << 1) = " << (b << 1) << endl;
- cout << "(b << 30) = " << (b << 30) << endl;
- return 0;
- }
編譯并運(yùn)行上面程序,結(jié)果如下:
- (b << 1) = -6
- (b << 30) = 1073741824
上面代碼中,變量 b 的值為 -3,對(duì)應(yīng)的二進(jìn)制是 1111 1111 1111 1111 1111 1111 1111 1101
左移 1 位:整個(gè)二進(jìn)制串向左移動(dòng) 1 位,右邊補(bǔ)充 0 ,左邊丟棄超出的一位二進(jìn)制,結(jié)果是 1111 1111 1111 1111 1111 1111 1111 1010,對(duì)應(yīng)十進(jìn)制數(shù) -6
左移 30 位 的流程如下
由上圖可知,左移 30 位,左邊的 30 位二進(jìn)制 1111 1111 1111 1111 1111 1111 1111 11 因超出數(shù)值最大位數(shù)而被丟棄,原來(lái)最右邊的 01 移到了最左邊,緊接著后面的 30 個(gè)空位全部補(bǔ) 0 ,最終的結(jié)果是 0100 0000 0000 0000 0000 0000 0000 0000 ,對(duì)應(yīng)十進(jìn)制是 1073741824
可以看出,負(fù)數(shù) -3 左移 30 位后,二進(jìn)制的符號(hào)位變成了 0 ,由開(kāi)始的負(fù)數(shù)變成了正數(shù)了
- 右移
負(fù)數(shù)右移是在左邊補(bǔ) 1, 所以結(jié)果不會(huì)出現(xiàn)正數(shù)的情況,如果一直右移,最終二進(jìn)制位會(huì)全部變成 1,即十進(jìn)制的 -1 ( 二進(jìn)制全 1 在補(bǔ)碼中表示 -1 )
修改 base.cpp,代碼如下
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t b = -3;
- cout << "(b >> 1) = " << (b >> 1) << endl;
- cout << "(b >> 31) = " << (b >> 31) << endl;
- return 0;
- }
編譯并運(yùn)行上面程序,結(jié)果如下:
- (b >> 1) = -2
- (b >> 31) = -1
上面代碼中,變量 b 的值為 -3,對(duì)應(yīng)的二進(jìn)制是 1111 1111 1111 1111 1111 1111 1111 1101
右移 1 位:二進(jìn)制位向右移動(dòng) 1 位,左邊補(bǔ)充 1 ,右邊丟棄一位超出的二進(jìn)制,結(jié)果為 1111 1111 1111 1111 1111 1111 1111 1110,對(duì)應(yīng)的十進(jìn)制是 -2
右移 31 位 的流程如下
根據(jù)上圖可知,右移 31 位,最右邊的 31 位二進(jìn)制 1111 1111 1111 1111 1111 1111 1111 110 因超出數(shù)值最大位數(shù)而被丟棄, 原來(lái)左邊的 1 移到了最右邊,左邊補(bǔ) 31 位 1,最后結(jié)果為:1111 1111 1111 1111 1111 1111 1111 1111 , 對(duì)應(yīng)的十進(jìn)制數(shù) -1
移位數(shù)超過(guò)類型最大位數(shù)
移位的位數(shù)大于等于數(shù)值類型的最大位數(shù)時(shí),實(shí)際的移的位數(shù)是:移位的位數(shù)和該類型的最大位數(shù)做取模運(yùn)算,余數(shù)就是要移的位數(shù),不管左移還是右移,這種方法都適用
比如:類型為 int32_t,移位的位數(shù)是 34,實(shí)際移位的位數(shù)為:34 % 32 = 1
修改 base.cpp 文件,內(nèi)容如下:
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t a = 7;
- cout << "(a >> 32) = " << (a >> 32) << endl;
- cout << "(a >> 33) = " << (a >> 33) << endl;
- cout << "(a << 34) = " << (a << 34) << endl;
- return 0;
- }
編譯并執(zhí)行,結(jié)果如下
- (a >> 32) = 7
- (a >> 33) = 3
- (a << 34) = 28
變量 a 的值為 7,對(duì)應(yīng)的二進(jìn)制是 0000 0000 0000 0000 0000 0000 0000 0111
右移 32 位:因移位位數(shù)等于 int32_t 最大位數(shù),所以實(shí)際移位數(shù)為 32 % 32 = 0,右移 0 位 表示沒(méi)有移位,所以結(jié)果還是 7
右移 33 位:移位位數(shù)大于 int32_t 最大位數(shù),故實(shí)際移位數(shù)為 33 % 32 = 1,右移 1 位,左邊補(bǔ) 0 ,右邊丟棄超出的位,結(jié)果是: 0000 0000 0000 0000 0000 0000 0000 0011,對(duì)應(yīng)的十進(jìn)制是 3
左移 34 位:移位位數(shù)大于 32 ,實(shí)際移位數(shù)為 34 % 32 = 2,左移 2 位,右邊補(bǔ) 0 ,左邊丟棄超出的位,結(jié)果是:0000 0000 0000 0000 0000 0000 0001 1100,對(duì)應(yīng)的十進(jìn)制是 28
正常情況下,在移位數(shù)相同時(shí),分幾次移位操作和單次移位操作的結(jié)果是一樣的, 比如:一個(gè) int32_t 變量,值為 7, 7 << 1 << 2 和 7 << 3 的結(jié)果相同的
當(dāng)移位數(shù)大于等于數(shù)值類型最大位數(shù)時(shí),上述的結(jié)果是不一樣的,比如:一個(gè) int32_t 變量,值為 7, 雖然 7 << 33 和 7 << 20 << 13 兩者移位數(shù)都是 33,但結(jié)果卻是不同的,前者是 14,而后者是 0
移位的位數(shù)是負(fù)值
當(dāng)移位的位數(shù)是負(fù)值時(shí),實(shí)際移位的位數(shù)是:用被移位數(shù)值類型的最大位數(shù)和移位位數(shù)相加,如果結(jié)果還是負(fù)數(shù),結(jié)果繼續(xù) 加上被移位數(shù)值類型的最大位數(shù),直到結(jié)果不為負(fù)數(shù)為止,此時(shí)的結(jié)果即為最終移位的位數(shù)
比如:被移位的數(shù)據(jù)類型是 int32_t,移位位數(shù)是 -31,最終移位的位數(shù)是:32 + ( -31 ) = 1
當(dāng)移位位數(shù)是 -60,計(jì)算最終移位位數(shù),32 + ( -60 ) = -28,由于結(jié)果還是負(fù)數(shù),所以繼續(xù)相加,32 + ( -28 ) = 4,此次結(jié)果不為負(fù)數(shù)了,所以最終移位的位數(shù)是 4
修改 base.cpp 文件,內(nèi)容如下:
- #include <stdint.h>
- #include <iostream>
- using namespace std;
- int32_t main(int32_t argc , char *argv[])
- {
- int32_t a = 7;
- cout << "(a >> -31) = " << (a >> -31) << endl;
- cout << "(a << -60) = " << (a >> -60) << endl;
- return 0;
- }
編譯代碼并執(zhí)行,結(jié)果如下
- (a >> -31) = 3
- (a >> -60) = 0
變量 a 的值為 7,對(duì)應(yīng)的二進(jìn)制是 0000 0000 0000 0000 0000 0000 0000 0111
右移 -31 位:移位數(shù)是負(fù)數(shù),實(shí)際右移 32 + ( -31 ) = 1 位,結(jié)果為:0000 0000 0000 0000 0000 0000 0000 0011,對(duì)應(yīng)的十進(jìn)制是 3
右移 -60 位:移位數(shù)是負(fù)數(shù),實(shí)際右移 32 + 32 + ( -60 ) = 4 位,結(jié)果為:0000 0000 0000 0000 0000 0000 0000 0000,對(duì)應(yīng)的十進(jìn)制是 0
小結(jié)
本文主要介紹了左移和右移操作,左移相當(dāng)于乘以 2 的 N 次方,而右移相當(dāng)于除以 2 的 N 次方,這里的 N 表示移位的位數(shù),需要注意的是,當(dāng)移位位數(shù)是負(fù)數(shù)或者大于等于類型最大位數(shù)時(shí),編譯器對(duì)他們的處理和正常的移位是不一樣的