JS位運(yùn)算符

起因

之前對(duì)js的一些涉及到二進(jìn)制的運(yùn)算符一直似懂非懂,看到了就一臉懵逼,還得去控制臺(tái)算一下。然后最近看算法的時(shí)候又看到了這個(gè)運(yùn)算符,這里就簡(jiǎn)單介紹一下學(xué)習(xí)這些位運(yùn)算符的過(guò)程。
注意:以下運(yùn)算均不涉及到小數(shù)。

過(guò)程

移位運(yùn)算符

<<

“<<”運(yùn)算符執(zhí)行無(wú)符號(hào)左移位運(yùn)算。在移位運(yùn)算過(guò)程中,符號(hào)位始終保持不變。如果右側(cè)空出位置,則自動(dòng)填充為 0;超出 32 位的值,則自動(dòng)丟棄。

先說(shuō)這句話是什么意思。左移位是二進(jìn)制的一種運(yùn)算,就是在不改變二進(jìn)制數(shù)值32位長(zhǎng)度的前提下,將每位的數(shù)字都向左移動(dòng),左邊移出去的直接丟棄,右邊空出來(lái)的位置用0填充。無(wú)符號(hào)就是保持符號(hào)位不變,即本來(lái)是正數(shù),移位后一樣為正數(shù)。

正數(shù)的無(wú)符號(hào)左移位運(yùn)算

這里以 7 << 2 為例。

首先將7轉(zhuǎn)為二進(jìn)制是 0000 0000 0000 0000 0000 0000 0000 0111.
然后對(duì)其向左移兩位.

                0000 0000 0000 0000  0000 0000 0000 0111    --> 7
                                                    
    << 2     00 0000 0000 0000 0000  0000 0000 0001 11      --> 左邊超出部分移除,坐標(biāo)填充0
    ----------------------------------------------------
    =           0000 0000 0000 0000  0000 0000 0001 1100    --> 28

得到值為 0000 0000 0000 0000 0000 0000 0001 1100.
轉(zhuǎn)換為十進(jìn)制為 28.即 7 << 2 = 28

然后我們對(duì)以上的運(yùn)算過(guò)程做一個(gè)處理,將這些二進(jìn)制轉(zhuǎn)換為我們熟悉的十進(jìn)制。

                0000 0000 0000 0000  0000 0000 0000 0111 = 2^2 + 2^1 + 2^0 = 7
    << 2     00 0000 0000 0000 0000  0000 0000 0001 1100 = 2^4 + 2^3 + 2^2 = 28  

對(duì)移位后的算式進(jìn)行合并項(xiàng)可得到 2^4 + 2^3 + 2^2 = (2^2 + 2^1 + 2^0) * 2^2,即 2^4 + 2^3 + 2^2 = (2^2 + 2^1 + 2^0) * 2^2 = 7 * 2^2。由此我們可得出 7 << 2 = 7 * 2^2 = 28。
我們通過(guò)計(jì)算幾個(gè)簡(jiǎn)單的左移位運(yùn)算,與標(biāo)準(zhǔn)答案進(jìn)行比較,驗(yàn)證一下這個(gè)結(jié)論。

4 << 1 = 4 * 2^1 = 8
12 << 1 = 12 * 2^1 = 24
66 << 3 = 66 * 2^3 = 528
100 << 5 = 100 * 2^5 = 3200
9999 << 6 = 9999 * 2^6 = 639936

在控制臺(tái)中以上幾個(gè)算式的結(jié)果為

控制臺(tái)左移位運(yùn)算

答案完全一致。說(shuō)明我們的結(jié)論是正確的。當(dāng)然這個(gè)結(jié)論僅限于那些二進(jìn)制移位不會(huì)左移移出的數(shù)字的簡(jiǎn)單運(yùn)算。當(dāng)我們遇到一些簡(jiǎn)單的可以口算的左移位運(yùn)算時(shí)就可以使用這個(gè)結(jié)論快速得到結(jié)果,如果對(duì)于 99999 << 66 這種較復(fù)雜的運(yùn)算你也用這個(gè)結(jié)論計(jì)算,也沒(méi)有人會(huì)介意。

負(fù)數(shù)的無(wú)符號(hào)左移位運(yùn)算

下面我們看一下負(fù)數(shù)的左移位運(yùn)算。以 -66 << 2 為例。
首先,我們先復(fù)習(xí)一下負(fù)數(shù)如何轉(zhuǎn)換為二進(jìn)制。
負(fù)數(shù)轉(zhuǎn)換為二進(jìn)制的步驟有三:

  1. 確定負(fù)數(shù)對(duì)應(yīng)正數(shù)的二進(jìn)制
  2. 求第一步得到的二進(jìn)制的反碼
  3. 第二步得到的二進(jìn)制加一
-66
    66      -> 0000 0000 0000 0000  0000 0000 0100 0010
    反碼    -> 1111 1111 1111 1111  1111 1111 1011 1101
    +1      -> 1111 1111 1111 1111  1111 1111 1011 1110

然后對(duì)其向左移兩位.

                1111 1111 1111 1111  1111 1111 1011 1110    --> -66

    << 2     11 1111 1111 1111 1111  1111 1110 1111 10      --> 左邊超出部分移除,坐標(biāo)填充0
    ----------------------------------------------------
    =           1111 1111 1111 1111  1111 1110 1111 1000    --> 28

得到值為 1111 1111 1111 1111 1111 1110 1111 1000.然后我們將其轉(zhuǎn)換成十進(jìn)制。

1111 1111 1111 1111  1111 1110 1111 1000
    -1     -> 1111 1111 1111 1111  1111 1110 1111 0111
    反碼   -> 0000 0000 0000 0000 0000 0001 0000 1000
    轉(zhuǎn)換   -> -264                                       --> 別忘了帶符號(hào)

轉(zhuǎn)換為十進(jìn)制為 -264.即 -66 << 2 = -264。

拓展
  1. 拓展1

剛剛我們計(jì)算 -66 的二進(jìn)制得到的是 1111 1111 1111 1111 1111 1111 1011 1110。我們?cè)诳刂婆_(tái)驗(yàn)證一下我們得到的這個(gè)二進(jìn)制。

-66的二進(jìn)制表示

欸,這差距有點(diǎn)大呀。是我們算的不對(duì)嗎?肯定不是。是因?yàn)閖s的引擎在做處理的時(shí)候是先按無(wú)符號(hào)數(shù)字進(jìn)行處理,轉(zhuǎn)換完了才會(huì)加上符號(hào)。所以 -66 的二進(jìn)制應(yīng)該是先得出66的二進(jìn)制,然后加上負(fù)號(hào),就得到了 -1000010

  1. 拓展2

我們比較一下下面幾個(gè)算式。

33 << 2 = 132
-33 << 2 = -132
66 << 2 = 264
-66 << 2 = -264
999 << 2 = 3996
-999 << 2 = -3996

是的沒(méi)錯(cuò),進(jìn)行無(wú)符號(hào)左移位運(yùn)算時(shí),當(dāng)兩個(gè)數(shù)的絕對(duì)值相等時(shí),其相同位數(shù)的移位的絕對(duì)值一定相等。

>>

“>>”運(yùn)算符執(zhí)行有符號(hào)右移位運(yùn)算。與左移運(yùn)算操作相反,它把 32 位數(shù)字中的所有有效位整體右移,再使用符號(hào)位的值填充空位。移動(dòng)過(guò)程中超出的值將被丟棄。

正數(shù)的有符號(hào)右移位運(yùn)算

這里以 666 >> 3 為例。

首先將666轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0010 1001 1010。
然后對(duì)其向右移三位。

            0000 0000 0000 0000  0000 0010 1001 1010        --> 666
                                                    
    >> 3       0 0000 0000 0000  0000 0000 0101 0011 010    --> 因?yàn)槭钦龜?shù)所以左邊填充0,右邊超出部分移除
    ------------------------------------------------
    =       0000 0000 0000 0000  0000 0000 0101 0011        --> 83

得到值為 0000 0000 0000 0000 0000 0000 0101 0011.
轉(zhuǎn)換為十進(jìn)制為 83.即 666 >> 3 = 83

然后我們對(duì)以上的運(yùn)算過(guò)程做一個(gè)處理,將這些二進(jìn)制轉(zhuǎn)換為我們熟悉的十進(jìn)制。

            0000 0000 0000 0000  0000 0010 1001 1010        = 2^9 + 2^7 + 2^4 + 2^3 + 2^1 = 666
    >> 3       0 0000 0000 0000  0000 0000 0101 0011 010    = 2^6 + 2^4 + 2^1 + 2^0       = 28  

這個(gè)規(guī)律好像不太好總結(jié)?

負(fù)數(shù)的有符號(hào)右移位運(yùn)算

這里以 -666 >> 3為例。

因?yàn)槭怯蟹?hào)的運(yùn)算,所以這里不再適用上一小節(jié)說(shuō)的js的特殊處理。先將-666轉(zhuǎn)換為二進(jìn)制。

-666
    666     -> 0000 0000 0000 0000  0000 0010 1001 1010
    反碼    -> 1111 1111 1111 1111  1111 1101 0110 0101
    +1      -> 1111 1111 1111 1111  1111 1101 0110 0110

即-666的二進(jìn)制形式為 1111 1111 1111 1111 1111 1101 0110 0110,然后對(duì)其進(jìn)行有符號(hào)右移位運(yùn)算

            1111 1111 1111 1111  1111 1101 0110 0110        --> -666
                                                    
    >> 3       1 1111 1111 1111  1111 1111 1010 1100 110    --> 因?yàn)槭秦?fù)數(shù)所以左邊填充1,右邊超出部分移除
    ------------------------------------------------
    =       1111 1111 1111 1111  1111 1111 1010 1100        --> -84

移位后得到的值為 1111 1111 1111 1111 1111 1111 1010 1100,是一個(gè)負(fù)值,我們將其轉(zhuǎn)成十進(jìn)制。

1111 1111 1111 1111  1111 1111 1010 1100
    -1     -> 1111 1111 1111 1111  1111 1111 1010 1011
    反碼   -> 0000 0000 0000 0000 0000 0000 0101 0100
    轉(zhuǎn)換   -> -84                                       --> 別忘了帶符號(hào)

我們對(duì)此結(jié)果進(jìn)行驗(yàn)證。

控制臺(tái)有符號(hào)右移位運(yùn)算驗(yàn)證

可見(jiàn),我們的運(yùn)算是完全正確的。

>>>

“>>>”運(yùn)算符執(zhí)行無(wú)符號(hào)右移位運(yùn)算。它把無(wú)符號(hào)的 32 位整數(shù)所有效位整體右移。對(duì)于無(wú)符號(hào)數(shù)或正數(shù)右移運(yùn)算,無(wú)符號(hào)右移與有符號(hào)右移運(yùn)算的結(jié)果是相同的。

正數(shù)無(wú)符號(hào)右移位運(yùn)算

這里我們以 666 >>> 3為例。

首先將666轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0010 1001 1010
然后對(duì)其向右移三位。

            0000 0000 0000 0000  0000 0010 1001 1010        --> 666
                                                    
    >> 3       0 0000 0000 0000  0000 0000 0101 0011 010    --> 左邊空出部分填充0,右邊超出部分移除
    ------------------------------------------------
    =       0000 0000 0000 0000  0000 0000 0101 0011        --> 83

得到值為 0000 0000 0000 0000 0000 0000 0101 0011.
轉(zhuǎn)換為十進(jìn)制為 83.即 666 >> 3 = 83

負(fù)數(shù)無(wú)符號(hào)右移位運(yùn)算

這里以 -666 >> 3為例。

因?yàn)槭怯蟹?hào)的運(yùn)算,所以這里不再適用上一小節(jié)說(shuō)的js的特殊處理。先將-666轉(zhuǎn)換為二進(jìn)制。

-666
    666     -> 0000 0000 0000 0000  0000 0010 1001 1010
    反碼    -> 1111 1111 1111 1111  1111 1101 0110 0101
    +1      -> 1111 1111 1111 1111  1111 1101 0110 0110

即-666的二進(jìn)制形式為 1111 1111 1111 1111 1111 1101 0110 0110,然后對(duì)其進(jìn)行有符號(hào)右移位運(yùn)算

            1111 1111 1111 1111  1111 1101 0110 0110        --> -666
                                                    
    >> 3       1 1111 1111 1111  1111 1111 1010 1100 110    --> 左邊空出部分填充0,右邊超出部分移除
    ------------------------------------------------
    =       0001 1111 1111 1111  1111 1111 1010 1100        --> 536870828

移位后得到的值為 0001 1111 1111 1111 1111 1111 1010 1100,轉(zhuǎn)成十進(jìn)制為536870828。
是不是超級(jí)大。因?yàn)槭菬o(wú)符號(hào)右移位運(yùn)算,所以在左邊空出部分不論正負(fù)都會(huì)填充0.

我們對(duì)此結(jié)果進(jìn)行驗(yàn)證。

控制臺(tái)無(wú)符號(hào)右移位運(yùn)算驗(yàn)證

可見(jiàn),我們的運(yùn)算是完全正確的。

注意:因?yàn)閷?duì)負(fù)數(shù)進(jìn)行無(wú)符號(hào)右移位運(yùn)算時(shí),所得結(jié)果很大,所以在使用過(guò)程中需要格外注意。

疑問(wèn):左移位和右移位根本都是只對(duì)位置進(jìn)行了移動(dòng),那么對(duì)于 x1 >> k = y1y2 << k = x2 中的 x1 等于 x2y1 等于 y2 嗎?

不一定。因?yàn)槲覀儾荒艽_保移動(dòng)過(guò)程中被丟棄的值均為0。但凡有一個(gè)1被丟棄,就不會(huì)相等。而如果被丟棄的都是0,那么 x1 === x2 y1 === y2。如下圖所示。

控制臺(tái)左右移位運(yùn)算比較

邏輯位運(yùn)算符

&

“&”運(yùn)算符(位與)用于對(duì)兩個(gè)二進(jìn)制操作數(shù)逐位進(jìn)行比較,并根據(jù)下表所示的換算表返回結(jié)果。

第一個(gè)數(shù)的位值 第二個(gè)數(shù)的位值 運(yùn)算結(jié)果
1 1 1
1 0 0
0 1 0
0 0 0

這里以 66 & 33 為例。

首先將兩個(gè)數(shù)轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0000 0100 00100000 0000 0000 0000 0000 0000 0010 0001。
然后對(duì)其進(jìn)行與運(yùn)算。

        0000 0000 0000 0000  0000 0000 0100 0010
    &   0000 0000 0000 0000  0000 0000 0010 0001
    --------------------------------------------    --> 按上述表格進(jìn)行運(yùn)算
        0000 0000 0000 0000  0000 0000 0000 0000    --> 0

得出結(jié)果為 0.

負(fù)數(shù)的與運(yùn)算與正數(shù)并無(wú)區(qū)別,不做討論。

|

“|”運(yùn)算符(位或)用于對(duì)兩個(gè)二進(jìn)制操作數(shù)逐位進(jìn)行比較,并根據(jù)如表格所示的換算表返回結(jié)果。

第一個(gè)數(shù)的位值 第二個(gè)數(shù)的位值 運(yùn)算結(jié)果
1 1 1
1 0 1
0 1 1
0 0 0

這里以 66 | 66 為例。

首先將兩個(gè)數(shù)轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0000 0100 00100000 0000 0000 0000 0000 0000 0100 0010。
然后對(duì)其進(jìn)行與運(yùn)算。

        0000 0000 0000 0000  0000 0000 0100 0010
    |   0000 0000 0000 0000  0000 0000 0100 0010
    --------------------------------------------    --> 按上述表格進(jìn)行運(yùn)算
        0000 0000 0000 0000  0000 0000 0100 0010    --> 66

得出結(jié)果為 66.

負(fù)數(shù)的與運(yùn)算與正數(shù)并無(wú)區(qū)別,不做討論。

^

“^”運(yùn)算符(位異或)用于對(duì)兩個(gè)二進(jìn)制操作數(shù)逐位進(jìn)行比較,并根據(jù)如表格所示的換算表返回結(jié)果。

第一個(gè)數(shù)的位值 第二個(gè)數(shù)的位值 運(yùn)算結(jié)果
1 1 0
1 0 1
0 1 1
0 0 0

這里以 66 ^ 66 為例。

首先將兩個(gè)數(shù)轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0000 0100 00100000 0000 0000 0000 0000 0000 0100 0010。
然后對(duì)其進(jìn)行與運(yùn)算。

        0000 0000 0000 0000  0000 0000 0100 0010
    |   0000 0000 0000 0000  0000 0000 0100 0010
    --------------------------------------------    --> 按上述表格進(jìn)行運(yùn)算
        0000 0000 0000 0000  0000 0000 0000 0000    --> 0

得出結(jié)果為 0.

負(fù)數(shù)的與運(yùn)算與正數(shù)并無(wú)區(qū)別,不做討論。

~

“~”運(yùn)算符(位非)用于對(duì)一個(gè)二進(jìn)制操作數(shù)逐位進(jìn)行取反操作。

這里以 ~66 為例。

首先將其轉(zhuǎn)換為二進(jìn)制是 0000 0000 0000 0000 0000 0000 0100 0010
然后對(duì)其進(jìn)行與運(yùn)算。

    ~   0000 0000 0000 0000  0000 0000 0100 0010
    --------------------------------------------    --> 對(duì)每一位都進(jìn)行取反操作
        1111 1111 1111 1111  1111 1111 1011 1101    --> -67

將結(jié)果(1111 1111 1111 1111 1111 1111 1011 1101)轉(zhuǎn)換為十進(jìn)制

1111 1111 1111 1111  1111 1111 1011 1101
    -1     -> 1111 1111 1111 1111  1111 1111 1011 1100
    反碼   -> 0000 0000 0000 0000  0000 0000 0100 0011
    轉(zhuǎn)換   -> -67                                       --> 別忘了符號(hào)

得出結(jié)果為 -67.

這里我們?cè)傥铱磶讉€(gè)例子。

~2 = -3
~10 = -11
~66 = -67
~99 = -100
~-99 = 98

從中我們可以看出,位非操作就是對(duì)數(shù)字加一,然后取負(fù)。我們可以寫(xiě)個(gè)簡(jiǎn)單的判斷方法來(lái)驗(yàn)證。

function judgeResult(num) {
    return ~num === -(num + 1)
}

judgeResult() // false
judgeResult(10) // true
judgeResult(1) // true
judgeResult(-66) // true
judgeResult(324) // true

總結(jié)

位運(yùn)算符運(yùn)算結(jié)果非常有趣,在平時(shí)可以多加應(yīng)用,但是一定要注意可能產(chǎn)生大數(shù)的預(yù)算,避免產(chǎn)生不必要的BUG。
這篇文章只是做了一個(gè)簡(jiǎn)單的介紹。后面有空了會(huì)做一下在實(shí)際開(kāi)發(fā)中的應(yīng)用,雖然我可能很久都遇不到。

參考文獻(xiàn)

JavaScript學(xué)習(xí)指南:JS入門(mén)教程

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容