聊聊JavaScript中的二進制數(shù)

2018-06-17 11:07 更新

事情的起因是這樣的最近在業(yè)務(wù)代碼中發(fā)現(xiàn)下面這樣的一行代碼,我看了半天沒搞明白是什么意思,不知道聰明的你能不能知道是什么意思呢?

!~location.href.search('***')

如果你也不知道,并且也像我一樣富有好奇心那么就和我一起來學(xué)習(xí)這篇文章吧。在本文中你將學(xué)到如下知識:

  • 二進制數(shù)的表示
  • js中的二進制數(shù)整數(shù)
  • js中的位運算

二進制數(shù)

本文假設(shè)你知道計算機中用二進制數(shù)來存儲,計算數(shù)字,并且熟悉二進制數(shù)的表示方法。

為了實現(xiàn)不同的目的,其實都是為了簡化問題,二進制數(shù)在計算機中有不同的表示方法,如原碼、反碼、補碼和移碼等。

注意:本文問了簡化運算,二進制數(shù)都是用一個字節(jié)——8個二進制位來簡化說明

先來說說真值吧,我們表示自然數(shù)包括正數(shù),負(fù)數(shù)和0,下面是1和-1的二進制表示,我們稱為真值

+ 00000001 # +1
- 00000001 # -1

8位二進制數(shù)能表示的真值范圍是[-2^8, +2^8]。

由于計算機只能存儲0和1,不能存儲正負(fù),所以用8個二進制位的最高位來表示符號,0表示正,1表示負(fù),用后七位來表示真值的絕對值,這種表示方法稱為原碼表示法,簡稱原碼,上面的1和-1的原碼如下:

0 0000001 # +1
1 0000001 # -1

由于10000000的意思是-0,這個沒有意義,所有這個數(shù)字被用來表示-128,所有負(fù)數(shù)就比整數(shù)多一個。

由于最高位被用來表示符號了,現(xiàn)在能表示的范圍是[-2^7, +2^7-1],即[-128, +127]

反碼是另一種表示數(shù)字的方法,其規(guī)則是整數(shù)的反碼何其原碼一樣,負(fù)數(shù)的反碼將其原碼的符號位不變,其余各位按位取反

0 0000001 # +1
1 1111110 # -1

反碼的表示范圍是[-2^7, +2^7-1],即[-128, +127]

補碼是另外一種表示方法,主要是為了簡化運算,將減法變?yōu)榧臃ǘl(fā)明的數(shù)字表示法,其規(guī)則是整數(shù)的補碼和原碼一樣,負(fù)數(shù)的補碼是其反碼末尾加1

0 0000001 # +1
1 1111111 # -1

快速計算負(fù)數(shù)補碼的規(guī)則就是,由其原碼低位向高位找到第一個1,1和其低位不變,1前面的高位按位取反即可,不知道聰明的你能不能想到原理。

8位補碼表示的范圍是[-2^7, +2^7-1],即[-128, +127]

js中的二進制數(shù)整數(shù)

再來說說js中的二進制整數(shù)表示,一名合格的jser應(yīng)該支持在js中只有一種數(shù)字類型,就是浮點型,js的浮點數(shù)遵循IEEE 754規(guī)范,如果你想了解js浮點數(shù)的更多知識,我推薦你看這篇文章《每一個JavaScript開發(fā)者應(yīng)該了解的浮點知識》。

然而在js中還有另一種類型的數(shù)據(jù),那就是用32個比特位表示的整數(shù),只要對js中的任何數(shù)字做位運算操作系統(tǒng)內(nèi)部都會將其轉(zhuǎn)換成整形,嘗試在控制臺輸入下面的代碼

2.1 | 0 # 或運算
>>> 2

js中的這種整形是區(qū)分正負(fù)數(shù)的,我們根據(jù)上面的知識推斷js中的整數(shù)的表示范圍是[-2^31, +2^31-1],即[-2147483648, +2147483647],在控制臺輸出下面的代碼來驗證我們的推斷

-2147483648 | 0
>>> -2147483648

-2147483649 | 0
>>> 2147483647

2147483647 | 0
>>> 2147483647

2147483648 | 0
>>> -2147483648

從上面的結(jié)果可以看出,大于和小于最低和最高的值再去進行轉(zhuǎn)換時都將改變正負(fù)號

js中的位運算

js中的位運算符有下面這些,對數(shù)字進行這些操作時,系統(tǒng)內(nèi)部都會講64的浮點數(shù)轉(zhuǎn)換成32位的整形

  • & 與
  • | 或
  • ~ 非
  • ^ 異或
  • << 左移
  • >> 算數(shù)右移(有符號右移)
  • >>> 邏輯右移(無符號右移)

下面舉例子來說明每個運算符的作用,開始之前先來介紹幾個會用到的知識點

原生二進制字面量

es6中引入了原生二進制字面量,二進制數(shù)的語法是0b開頭,我們將會用到這個新功能,目前chrome最新版已經(jīng)支持。

0b111 // 7
0b001 // 1

Number.prototype.toString

先來介紹下下面會用到的一個方法——Number.prototype.toString方法可以講數(shù)字轉(zhuǎn)化為字符串,有一個可選的參數(shù),用來決定將數(shù)字顯示為指定的進制,下面可以查看3的二進制表示,根據(jù)這個特性,我還特意做了一個進制轉(zhuǎn)化工具。

3..toString(2)
>> 11

& 與

&按位與會將操作數(shù)和被操作數(shù)的相同為進行與運算,如果都為1則為1,如果有一個為0則為0

101
011
---
001

101和011與完的結(jié)果就是001,下面在js中進行驗證

(0b101 & 0b011).toString(2)
>>> "1"

| 或

|按位或是相同的位置上只要有一個為1就是1,兩個都為0則為0

101
001
---
101

101和001或完的結(jié)果是101,下面在js中進行驗證

(0b101 | 0b001).toString(2)
>>> "101"

~ 非

~操作符會將操作數(shù)的每一位取反,如果是1則變?yōu)?,如果是0則邊為1

101
---
010

101按位非的結(jié)果是010,下面在js中驗證

(~0b101).toString(2)
>>> "-110"

啊呀,怎么結(jié)果不對呢?。?!上面提到了js中的數(shù)字是有符號的,我們忘記了最高位的符號了,為了簡化我們將32位簡化為8位,注意最高位是符號位

0 0000101
1 1111010 // 非后的結(jié)果
1 0000101 // 求反
1 0000110 // 求補

1 1111010明顯是一個負(fù)數(shù),而且是負(fù)數(shù)的補碼表示,我們的求它的原碼,也就是再對它求補1 0000110就是這個數(shù)的真值,也就是結(jié)果顯示-110,這下總算自圓其說了,O(∩_∩)O哈哈~

其實上面的與和或也都是會操作符號位的,不信你試試下面這兩個,可以看到符號位都參與了運算

(0b1&-0b1)
>>> 1

(0b1|-0b1)
>>> -1

^ 異或

再來說說異或,這個比較有意思,異或顧名思義看看兩個位是否為異——不同,兩個位不同則為1,兩個位相同則為0

101
001
---
100

101和001異或的結(jié)果是100,js中驗證

(0b101^0b001).toString(2)
>>> "100"

<< 左移

左移的規(guī)則就是每一位都向左移動一位,末尾補0,其效果相當(dāng)于×2,其實計算機就是用移位操作來計算乘法的

010
---
0100

010左移一位就會變?yōu)?00,下面在js中驗證

(0b010<<1).toString(2)
>>> "100"

>> 算數(shù)右移(有符號右移)

算數(shù)右移也稱為有符號右移,也就是移位的時候高位補的是其符號位,整數(shù)則補0,負(fù)數(shù)則補1

(0b111>>1).toString(2)
>>> "11"

(-0b111>>1).toString(2)
>>> "-100"

負(fù)數(shù)的結(jié)果好像不太對勁,我們來看看是怎么回事

-111 // 真值
1 0000111 // 原碼
1 1111001 // 補碼
1 1111100 // 算數(shù)右移
1 0000100 // 移位后的原碼
-100 // 移位后的真值

>>> 邏輯右移(無符號右移)

邏輯右移又稱為無符號右移,也就是右移的時候高位始終補0,對于整數(shù)和算數(shù)右移沒有區(qū)別

(0b111>>>1).toString(2)
>>> "11"

對于負(fù)數(shù)則就不同了,右移后會變?yōu)檎龜?shù)

(-0b111>>>1).toString(2)
>>> "1111111111111111111111111111100"

關(guān)于開頭的問題

關(guān)于二進制數(shù)就說這么多吧,再來說說開頭的問題,開頭的問題其實可以分解為下面的問題因為search會返回-1 和找到位置的索引,也就成了下面的問題

!~-1
>>> ture

!~0
>>> false

!~1
>>> false

非運算對于數(shù)字的結(jié)果相當(dāng)于改變符號,并對其值的絕對值-1

~-1
>>> 0

~0
>>> -1

~1
>>> -2

其實可以看出!~x的邏輯就是判斷x是否為-1,my god這邏輯真是逆天了,我還是勸大家直接寫成 x === -1多好啊

總結(jié)

通過這篇文章終于把當(dāng)年沒學(xué)明白的二進制數(shù)搞明白了,希望你和我一樣,祝你好運。

參考資料

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號