很多高級(jí)語(yǔ)言的開(kāi)發(fā)者都容易忽略位運(yùn)算符的使用技巧,因?yàn)樗麄兛偢杏X(jué)位運(yùn)算符是底層開(kāi)發(fā)的專(zhuān)利苦始;其實(shí),這是錯(cuò)誤的慌申,通過(guò)巧妙地使用位運(yùn)算符陌选,常常可以獲取性能上的提升和代碼的精簡(jiǎn)蹄溉;為了在 JavaScript 中充分利用位運(yùn)算符咨油,我挖掘了一些實(shí)用的技巧;本文就集中講解下位運(yùn)算符在 JavaScript 中的巧妙使用方法及其原理柒爵;
提示:如果想忽略基礎(chǔ)知識(shí)役电,直接看技巧,可從 五棉胀、取整
開(kāi)始看起
目錄
- 一法瑟、概念
- 機(jī)器數(shù)
- 真值
- 原碼
- 反碼
- 補(bǔ)碼
- 二、位運(yùn)算符的特性
- 三唁奢、位運(yùn)算符介紹
- 四霎挟、特殊的值
- 五、取整
- 方式1:與0進(jìn)行或運(yùn)算
x|0
- 方式2:與-1進(jìn)行與運(yùn)算
x&-1
- 方式3:與0異或
x^0
- 方式4:雙非運(yùn)算
~~x
- 方式5:與同一個(gè)數(shù)兩次異或
x^a^a
- 方式6:左移0位
x<<0
- 方式7:有符號(hào)右移0位
x>>0
- 方式8:對(duì)正數(shù)無(wú)符號(hào)右移0位
x>>>0
- 方式9:無(wú)符號(hào)右移0位再與0進(jìn)行或運(yùn)算
x>>>0|0
- 方式10:無(wú)符號(hào)右移0位再與0進(jìn)行異或運(yùn)算
x>>>0^0
- 方式1:與0進(jìn)行或運(yùn)算
- 六麻掸、與2的冪相乘
- 七酥夭、位開(kāi)關(guān)
- 八、求對(duì)2的冪的余數(shù)
- 九、奇偶判斷
- 十采郎、交互兩個(gè)數(shù)
- 方法1:中間變量
- 方法2:表達(dá)式暫存
- 方法3:異或運(yùn)算
內(nèi)容
一千所、概念
在講解位算符的技巧和原理之前,需要選了解一下一些概念:
-
機(jī)器數(shù):
一個(gè)數(shù)在計(jì)算機(jī)中的二進(jìn)制表示形式, 叫做這個(gè)數(shù)的機(jī)器數(shù)蒜埋。機(jī)器數(shù)是帶符號(hào)的,在計(jì)算機(jī)用一個(gè)數(shù)的最高位存放符號(hào), 正數(shù)為0, 負(fù)數(shù)為1最楷;
比如整份,十進(jìn)制中的數(shù) +3 ,計(jì)算機(jī)字長(zhǎng)為8位籽孙,轉(zhuǎn)換成二進(jìn)制就是00000011烈评。如果是 -3 ,就是 10000011 犯建。
那么讲冠,這里的 00000011 和 10000011 就是機(jī)器數(shù)。 -
真值:
因?yàn)榈谝晃皇欠?hào)位适瓦,所以機(jī)器數(shù)的形式值就不等于真正的數(shù)值竿开。例如上面的有符號(hào)數(shù) 10000011,其最高位1代表負(fù)玻熙,其真正數(shù)值是 -3 而不是形式值131(10000011轉(zhuǎn)換成十進(jìn)制等于131)否彩。所以,為區(qū)別起見(jiàn)嗦随,將帶符號(hào)位的機(jī)器數(shù)對(duì)應(yīng)的真正數(shù)值稱(chēng)為機(jī)器數(shù)的真值列荔。
例:0000 0001的真值 = +000 0001 = +1,1000 0001的真值 = –000 0001 = –1
枚尼; -
原碼:
原碼就是符號(hào)位加上真值的絕對(duì)值, 即用第一位表示符號(hào), 其余位表示值.
比如:假設(shè)總共有8位:+1 的原碼 = 0000 0001 -1 的原碼 = 1000 0001
-
反碼:
反碼的表示方法是:- 正數(shù)的反碼是其原碼贴浙;
- 負(fù)數(shù)的反碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變,其余各個(gè)位取反署恍;
+1 : 反碼是 [00000001]崎溃,原碼是 [00000001] -1 : 反碼是 [11111110],原碼是 [10000001]
-
補(bǔ)碼:
補(bǔ)碼的表示方法是:- 正數(shù)的補(bǔ)碼就是其本身锭汛;
- 負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上, 符號(hào)位不變, 其余各位取反, 然后加 1笨奠;即:在反碼的基礎(chǔ)上加 1;
+1 : 原碼是 [00000001]唤殴,反碼是 [00000001]般婆,補(bǔ)碼是 [00000001] -1 : 原碼是 [10000001],反碼是 [11111110]朵逝,補(bǔ)碼是 [11111111]
二蔚袍、位運(yùn)算符的特性
在 JavaScript 中,位運(yùn)算符具備以下特性:
- 操作數(shù)被轉(zhuǎn)換成有符號(hào)32位整數(shù),用比特序列(0和1組成)表示啤咽。超過(guò)32位的數(shù)字會(huì)被丟棄晋辆。
例如, 以下具有32位以上的整數(shù)將轉(zhuǎn)換為32位整數(shù):轉(zhuǎn)換前: 11100110111110100000000000000110000000000001 轉(zhuǎn)換后: 10100000000000000110000000000001
- 位運(yùn)算符操作的是操作數(shù)對(duì)應(yīng)的機(jī)器數(shù),而不是原碼宇整;
- 數(shù)值的機(jī)器數(shù)就是數(shù)值原碼對(duì)應(yīng)的補(bǔ)碼瓶佳;也就是說(shuō),位運(yùn)算符操作的是操作數(shù)對(duì)應(yīng)的補(bǔ)碼鳞青;
- JavaScript 中的數(shù)字類(lèi)型是 基于 IEEE 754 標(biāo)準(zhǔn)的雙精度 64 位二進(jìn)制格式的值(
-(2**63 -1)
到2**63 -1
)霸饲。但其能表示的最小值 和 最在值 是要比-(2**63 -1)
和2**63 -1
的絕對(duì)值大很多的,因?yàn)?JavaScript 中的數(shù)字可以用 科學(xué)計(jì)數(shù)法xEy
來(lái)表示臂拓;
三厚脉、位運(yùn)算符介紹
運(yùn)算符 | 用法 | 描述 |
---|---|---|
按位與(AND) | a & b |
對(duì)于每一個(gè)比特位,只有兩個(gè)操作數(shù)相應(yīng)的比特位都是1時(shí)胶惰,結(jié)果才為1傻工,否則為0。 |
按位或(OR) | a | b |
對(duì)于每一個(gè)比特位孵滞,當(dāng)兩個(gè)操作數(shù)相應(yīng)的比特位至少有一個(gè)1時(shí)中捆,結(jié)果為1,否則為0剃斧。 |
按位異或(XOR) | a ^ b |
對(duì)于每一個(gè)比特位轨香,當(dāng)兩個(gè)操作數(shù)相應(yīng)的比特位有且只有一個(gè)1時(shí),結(jié)果為1幼东,否則為0臂容。 |
按位非(NOT) | ~ a |
反轉(zhuǎn)操作數(shù)的比特位,即0變成1根蟹,1變成0脓杉。 |
左移(Left shift) | a << b |
將 a 的二進(jìn)制形式向左移 b (< 32) 比特位,右邊用0填充简逮。 |
有符號(hào)右移 | a >> b |
將 a 的二進(jìn)制表示向右移 b (< 32) 位球散,丟棄被移出的位。 |
無(wú)符號(hào)右移 | a >>> b |
將 a 的二進(jìn)制表示向右移 b (< 32) 位散庶,丟棄被移出的位蕉堰,并使用 0 在左側(cè)填充。 |
四悲龟、特殊的值
下面羅列了一些常用的特殊的值的 及 其對(duì)應(yīng)的機(jī)器數(shù) 和 其獲取方式屋讶;
-
0
:00000000 00000000 00000000 00000000
;機(jī)器數(shù)的所有位都是 0 须教;可通過(guò)~(-1)
獲取 -
-1
:11111111 11111111 11111111 11111111
皿渗; 機(jī)器數(shù)的所有位都是 1 斩芭; 可通過(guò)~0
或~NaN
獲取乐疆; -
-2147483648
:10000000 00000000 00000000 00000000
划乖;只有第一位(符號(hào)位)是 1 ,其它位都是 0 ; 可通過(guò)1<<31
獲燃吠痢琴庵; -
2147483647
:01111111 11111111 11111111 11111111
; 只有第一位(符號(hào)位)是 0 ,其它位都是 1 ; 可通過(guò)-1>>>1
或~(1<<31)
獲得仰美;
五细卧、取整
去掉浮點(diǎn)數(shù)的小數(shù)部分稱(chēng)為取整;
我們常用的取整方式如下:
- 通過(guò)
parseInt(x)
函數(shù)筒占; - 對(duì)正數(shù)使用
Math.ceil(x)
向下舍入; - 對(duì)負(fù)數(shù)使用
Math.floor(x)
向上舍入蜘犁;
這些函數(shù)都是通過(guò)對(duì)操作數(shù)應(yīng)用一定算法來(lái)實(shí)現(xiàn)的翰苫;下面提供的這些用位運(yùn)算符取整的方法,要比這些高效得多这橙,且簡(jiǎn)潔奏窑;
原理:
上面說(shuō)了,所有的位運(yùn)算符都會(huì)把操作數(shù)轉(zhuǎn)為 32 位整數(shù)屈扎;所以埃唯,當(dāng)位運(yùn)算符作用于浮點(diǎn)數(shù)時(shí),就會(huì)將和浮點(diǎn)數(shù)轉(zhuǎn)為32位整數(shù)鹰晨; 這是基本原理墨叛,但并不是所有的位運(yùn)算符都適合用于取整操作,因?yàn)橛行┻\(yùn)算符的操作過(guò)程是不可逆的模蜡,或者會(huì)改變操作數(shù)的整數(shù)部分的值漠趁;下面是我 設(shè)計(jì) 并 挑選 的幾個(gè)可用于取整的操作;
方式1:與0進(jìn)行或運(yùn)算 x|0
將一個(gè)數(shù) x 通過(guò)與 0 進(jìn)行或運(yùn)算 x ^ 0
忍疾,可直接得到 x 的整數(shù)部分闯传;
示例:
4.3 | 0 // 4
-4.6 | 0 // -4
原理:
根據(jù)上表中 或 定義,可得:1 | 0 == 1
和 0 | 0 == 0
卤妒;
即:任何位 x 與 0 或 的值都與原來(lái)位 x 相等甥绿;
而數(shù)值 0 對(duì)應(yīng)的機(jī)器數(shù)是 32 個(gè) 0 ,所以则披,與 0 或的位操作運(yùn)算導(dǎo)致了 操作數(shù) 去掉了小數(shù)部分共缕,而且不會(huì)改變操作數(shù)的整數(shù)部分的值;從而實(shí)現(xiàn)了取整的效果收叶;
方式2:與-1進(jìn)行與運(yùn)算x&-1
將一個(gè)數(shù) x 通過(guò)與 -1 進(jìn)行與運(yùn)算 x & -1
骄呼,可直接得到 x 的整數(shù)部分;
示例:
4.3 & -1 // 4
-4.6 & -1 // -4
原理:
根據(jù)上表中 與 定義,可得:1 & 1 == 1
和 0 & 1 == 0
蜓萄;
即:任何位 x 和 1 做 與運(yùn)算 的值都與原來(lái)位 x 相等隅茎;
而數(shù)值 -1 對(duì)應(yīng)的機(jī)器數(shù)是 32 個(gè) 1 ,所以嫉沽,與 -1 與的位操作運(yùn)算導(dǎo)致了 操作數(shù) 去掉了小數(shù)部分辟犀,而且不會(huì)改變操作數(shù)的整數(shù)部分的值;從而實(shí)現(xiàn)了取整的效果绸硕;
方式3:與0異或 x^0
將一個(gè)數(shù) x 通過(guò)與 0 異或 x ^ 0
堂竟,可直接得到 x 的整數(shù)部分;
示例:
4.3 ^ 0 // 4
-4.6 ^ 0 // -4
原理:
根據(jù)上表中 異或 定義玻佩,可得:1 ^ 0 == 1
和 0 ^ 0 == 0
出嘹;
即:任何位 x 與 0 異或 的值都與原來(lái)位 x 相等;
而數(shù)值 0 對(duì)應(yīng)的機(jī)器數(shù)是 32 個(gè) 0 咬崔,所以税稼,與 0 異或的位操作運(yùn)算導(dǎo)致了 操作數(shù) 去掉了小數(shù)部分,而且不會(huì)改變操作數(shù)的整數(shù)部分的值垮斯;從而實(shí)現(xiàn)了取整的效果郎仆;
方式4:雙非運(yùn)算 ~~x
對(duì)一個(gè)數(shù) x 進(jìn)行兩次非運(yùn)算,可直接得到該數(shù) x 的整數(shù)部分兜蠕;
示例:
~~4.3 // 4
~~(-4.6) // -4
原理:
非的位操作符使操作數(shù)去掉了小數(shù)部分扰肌,而雙非操作會(huì)使操作數(shù)的整數(shù)部分反轉(zhuǎn)之后再反轉(zhuǎn)從而變回了原來(lái)的整數(shù)值;
方式5:與同一個(gè)數(shù)兩次異或 x^a^a
對(duì)一個(gè)數(shù) x 進(jìn)行兩次非運(yùn)算熊杨,可直接得到該數(shù) x 的整數(shù)部分曙旭;
示例:
4.3^5^5 // 4
-4.6^-3^-3 // -4
原理:
根據(jù)上表中 異或 定義,可得:
1 ^ 0 == 1
0 ^ 0 == 0
1 ^ 1 == 0
0 ^ 1 == 1
即:
- 任何位 x 與 0 異或 的值 都與原來(lái)位 x 相等猴凹;
- 任何位 x 與 1 異或 的值 都與原來(lái)位 x 相反夷狰;
所以:
- 任何位 x 與 0 異或兩次 的值 都與原來(lái)位 x 相等;
- 任何位 x 與 1 異或兩次 相當(dāng)于將原來(lái)位 x 反轉(zhuǎn)兩次郊霎,即:與原來(lái)位 x 相等沼头;
所以:
- 任何數(shù) x 與 同一個(gè)數(shù) 異或兩次 的值 都與原來(lái)數(shù) x 相等;
所以:異或的位操作符使操作數(shù)去掉了小數(shù)部分书劝,而與同一個(gè)數(shù)雙異或的操作會(huì)使操作數(shù)的整數(shù)部分的值不變进倍;
方式6:左移0位 x<<0
將一個(gè)數(shù) x 左移0位,可直接得到該數(shù) x 的整數(shù)部分购对;
示例:
4.3<<0 // 4
-4.6<<0 // -4
原理:
左移的位操作符使操作數(shù)去掉了小數(shù)部分猾昆,而左移0位相當(dāng)于沒(méi)有移位,也使得保留了操作數(shù)的整數(shù)部分骡苞;
方式7:有符號(hào)右移0位 x>>0
對(duì)一個(gè)數(shù) x 進(jìn)行有符號(hào)右移0位垂蜗,可直接得到該數(shù) x 的整數(shù)部分楷扬;
示例:
4.3>>0 // 4
-4.6>>0 // -4
原理:
有符號(hào)右移的位操作符使操作數(shù)去掉了小數(shù)部分,而有符號(hào)右移0位相當(dāng)于即沒(méi)有移位也沒(méi)有改變操作數(shù)的符號(hào)位贴见,也使得保留了操作數(shù)的整數(shù)部分烘苹;
方式8:對(duì)正數(shù)無(wú)符號(hào)右移0位 x>>>0
對(duì)一個(gè)正數(shù) x 進(jìn)行無(wú)符號(hào)右移0位,可直接得到該數(shù) x 的整數(shù)部分片部;
示例:
4.3>>>0 // 4
原理:
無(wú)符號(hào)右移的位操作符使操作數(shù)去掉了小數(shù)部分镣衡,而無(wú)符號(hào)右移0位相當(dāng)于沒(méi)有移位,即:不會(huì)改變操作數(shù)的機(jī)器數(shù)档悠;但是會(huì)使操作數(shù)變成正數(shù)(即:計(jì)算機(jī)會(huì)把該機(jī)器數(shù)作為一個(gè)正數(shù)來(lái)看代)廊鸥;不過(guò),當(dāng)操作數(shù)本身就是正數(shù)時(shí)辖所,無(wú)符號(hào)右移動(dòng) 0 位就相當(dāng)于不會(huì)改變操作數(shù)的正負(fù)性惰说,所以也就實(shí)現(xiàn)了 保留正數(shù)操作數(shù)的整數(shù)部分;
方式9:無(wú)符號(hào)右移0位再與0進(jìn)行或運(yùn)算 x>>>0|0
對(duì)一個(gè)數(shù) x 進(jìn)行無(wú)符號(hào)右移0位后缘回,再與0進(jìn)行或運(yùn)算助被,可得到該數(shù) x 的整數(shù)部分;
示例:
4.3>>>0|0 // 4
-4.6>>>0|0 // -4
原理:
與 x>>>0^0
的原理相同:無(wú)符號(hào)右移的位操作符使操作數(shù)去掉了小數(shù)部分切诀,而無(wú)符號(hào)右移0位相當(dāng)于沒(méi)有移位,即:不會(huì)改變操作數(shù)的機(jī)器數(shù)搔弄;但是會(huì)使操作數(shù)變成正數(shù)(即:計(jì)算機(jī)會(huì)把該機(jī)器數(shù)作為一個(gè)正數(shù)來(lái)看代)幅虑;為了讓計(jì)算機(jī)把該機(jī)器數(shù)當(dāng)作一個(gè)有符號(hào)32位整數(shù),需要對(duì)該機(jī)器數(shù)應(yīng)用一次無(wú)變化位運(yùn)算顾犹,比如:與0相或倒庵;這樣,便可把機(jī)器數(shù)所表示的值恢復(fù)成原來(lái)的值炫刷;所以擎宝,所以也就實(shí)現(xiàn)了 保留操作數(shù)的整數(shù)部分;
方式10:無(wú)符號(hào)右移0位再與0進(jìn)行異或運(yùn)算 x>>>0^0
對(duì)一個(gè)數(shù) x 進(jìn)行無(wú)符號(hào)右移0位后浑玛,再與0進(jìn)行異或運(yùn)算绍申,可得到該數(shù) x 的整數(shù)部分;
示例:
4.3>>>0^0 // 4
-4.6>>>0^0 // -4
原理:
與 x>>>0^0
的原理相同:無(wú)符號(hào)右移的位操作符使操作數(shù)去掉了小數(shù)部分顾彰,而無(wú)符號(hào)右移0位相當(dāng)于沒(méi)有移位极阅,即:不會(huì)改變操作數(shù)的機(jī)器數(shù);但是會(huì)使操作數(shù)變成正數(shù)(即:計(jì)算機(jī)會(huì)把該機(jī)器數(shù)作為一個(gè)正數(shù)來(lái)看代)涨享;為了讓計(jì)算機(jī)把該機(jī)器數(shù)當(dāng)作一個(gè)有符號(hào)32位整數(shù)筋搏,需要對(duì)該機(jī)器數(shù)應(yīng)用一次無(wú)變化位運(yùn)算,比如:與0異或厕隧;這樣奔脐,便可把機(jī)器數(shù)所表示的值恢復(fù)成原來(lái)的值俄周;所以,所以也就實(shí)現(xiàn)了 保留操作數(shù)的整數(shù)部分髓迎;
六峦朗、與2的冪相乘
求一個(gè)數(shù) x 與 2 的 n 次方的冪 乘積 除了 用算術(shù)運(yùn)算符 x * (2 ** n)
的方式外,也可用位運(yùn)算符 向 左移 n 位 的方式:
x << n
示例
5 << 3 // 求 5 * 2 的 3 次方的 冪
原理
對(duì)于 二進(jìn)制的 移位操作竖般,每向左移一位甚垦,就相當(dāng)于乘了 基數(shù),即 2 涣雕,左移 n 位艰亮,就相當(dāng)于 乘了 n 次 基數(shù),即 基數(shù)的 n 次方挣郭,也即 2 的 n 次方迄埃,所以, 一個(gè)數(shù) 左移 n 位兑障,就相當(dāng)于 乘了 2的 n次方侄非;
七、位開(kāi)關(guān)
定義(個(gè)人定義流译,非官方):位開(kāi)關(guān) 就是 用二進(jìn)制數(shù)中一位來(lái)表示一個(gè)二態(tài)量逞怨;
假如,我們需要表示4個(gè)燈泡 A福澡、B叠赦、C、D 的狀態(tài)革砸;
通常除秀,比較直觀的設(shè)計(jì)方案是下面這樣:
- 用4個(gè)布爾變量 a、b算利、c册踩、d 分別表示 4 個(gè)燈泡的狀態(tài);
- 當(dāng)布爾值為 true 時(shí)效拭,表示燈是在亮著暂吉,當(dāng)布爾值為 false 時(shí),表示燈在關(guān)著缎患;
但是借笙,這樣設(shè)計(jì)的問(wèn)題是:當(dāng)我們需要傳遞 這些燈泡的狀態(tài)時(shí),我們需要傳遞 4 個(gè)參數(shù)较锡,比較麻煩业稼;
我們改用下面這個(gè)設(shè)計(jì)方案:
- 用一個(gè)數(shù) bulb 的一個(gè)二進(jìn)制位來(lái)表示一個(gè)燈泡的狀態(tài),比如:0 表示關(guān)蚂蕴、1 表示開(kāi)低散;
- 4個(gè)燈泡的狀態(tài)俯邓,則只需要一個(gè)4位二進(jìn)制位序列 DCBA 即可表示,如:二進(jìn)制數(shù)
0101
則表示:燈D:關(guān)熔号;燈C:開(kāi)稽鞭;燈B:關(guān);燈A:開(kāi)引镊;
這樣朦蕴,我們只需要用一個(gè)參數(shù) 就可以傳遞這些燈泡的狀態(tài);
獲取某個(gè)燈的狀態(tài):
如果我們想知道某個(gè)燈泡(比如 燈B)的狀態(tài)弟头,我們可以根據(jù)之前定義的位序列 DCBA 來(lái)寫(xiě)出 燈B 的位掩碼 mask = 0b0010
吩抓,然后將 表示燈狀態(tài)的數(shù) bulb 與 位掩碼 mask 做 位與運(yùn)算 bulb & mask
得到,如果值不為 0 赴恨,則表示 燈B 的狀態(tài)是 開(kāi)的疹娶;
判斷在某幾燈中,是否至少有一個(gè)是開(kāi)著的:
如果我們想知道某幾個(gè)燈泡(比如 燈C伦连、燈B)是否至少一個(gè)是開(kāi)著的雨饺,我們可以根據(jù)之前定義的位序列 DCBA 來(lái)寫(xiě)出 燈C、燈B 的位掩碼 mask = 0b0110
惑淳,然后將 表示燈狀態(tài)的數(shù) bulb 與 位掩碼 mask 做 位與運(yùn)算 bulb & mask
得到额港,如果 值不為 0 ,則表示 燈C 和 燈B 至少一個(gè)是開(kāi)著的歧焦;
判斷某些燈是否是都開(kāi)著的:
如果我們想知道某幾個(gè)燈泡(比如 燈C锹安、燈B)是否是都開(kāi)著的,我們可以根據(jù)之前定義的位序列 DCBA 來(lái)寫(xiě)出 燈C倚舀、燈B 的位掩碼 mask = 0b0110
,然后將 表示燈狀態(tài)的數(shù) bulb 與 位掩碼 mask 做 位與運(yùn)算忍宋,再將運(yùn)算的結(jié)果 與 位掩碼 mask 做相等比較 (bulb & mask) == mask
痕貌,如果值不為 true
,則表示 燈C 和 燈B 都是開(kāi)著的糠排;
總結(jié)
用一個(gè)二進(jìn)制位來(lái)表示一個(gè) 二態(tài)量舵稠,則多個(gè)二態(tài)量便可以用一個(gè)二進(jìn)制位序列來(lái)表示;這個(gè)二進(jìn)制位序列可以作為一個(gè)數(shù)存儲(chǔ)在一個(gè)變量中入宦;我將表示這樣二進(jìn)制序列的數(shù)叫做 開(kāi)關(guān)序列哺徊;
通過(guò)與 開(kāi)關(guān)序列 做 與
運(yùn)算 來(lái)獲取部分二態(tài)量信息的二進(jìn)制序列 叫做 這些二態(tài)量的 掩碼;掩碼 的表示方法是:將不需要的二態(tài)量的二進(jìn)制位置為 0 乾闰,將需要的二態(tài)量置為 1 落追,這樣得到的一個(gè)二進(jìn)制位序列 就是 這些所需量 的 掩碼;
獲取某個(gè)二態(tài)量的值
將 開(kāi)關(guān)序列 與 該二態(tài)量的 掩碼 做 位與 運(yùn)算涯肩,運(yùn)算的結(jié)果便是該二態(tài)量的值轿钠;
switchSeq & mask
示例:
var switchSeq = 0b0110; //包含多個(gè)二態(tài)量入信息的開(kāi)關(guān)序列
var mask = 0b0010; //第二個(gè)二態(tài)量的掩碼
var second = switchSeq & mask; //將 開(kāi)關(guān)序列 與 該二態(tài)量的 掩碼 做 位與 運(yùn)算 來(lái) 獲取第二個(gè)二態(tài)量的值
原理:
根據(jù)上表中 與 定義巢钓,可得:1 & 1 == 1
和 0 & 1 == 0
;
即:任何位 x 和 1 做 與運(yùn)算 的值都與原來(lái)位 x 相等疗垛;任何位 x 和 0 做 與運(yùn)算 的值是 0症汹;
而 掩碼中 不需要的二態(tài)量的二進(jìn)制位被置為了 0 ,在與 開(kāi)關(guān)序列 相與 時(shí)贷腕,不需要的位就被 置為了 0 背镇;掩碼中 需要的二態(tài)量置為 1 ,在與 開(kāi)關(guān)序列 相與 時(shí)泽裳,需要的位的值 就被保留了下來(lái) 瞒斩;所以,開(kāi)關(guān)序列 與 某個(gè)二態(tài)量的 掩碼 做 與運(yùn)算 后诡壁,得到的值就是 該二態(tài)量的值济瓢;
判斷是否至少包含某些二態(tài)量中的一個(gè)
將 開(kāi)關(guān)序列 與 這些二態(tài)量的 掩碼 做 位與 運(yùn)算 ,如果結(jié)果不為 0 妹卿,則表示 開(kāi)關(guān)序列 中 包含這些二態(tài)量中的至少一個(gè)旺矾;如果結(jié)果 為 0 ,則表示 開(kāi)關(guān)序列中 不包含 這些二態(tài)量中的任何一個(gè)夺克;
switchSeq & mask
示例:
var switchSeq = 0b0110; //包含多個(gè)二態(tài)量入信息的開(kāi)關(guān)序列
var mask = 0b0011; //第一箕宙、二個(gè)二態(tài)量的掩碼
var firstOrSecond = switchSeq & mask; //將 開(kāi)關(guān)序列 與 第一、二個(gè)二態(tài)量的 掩碼 做 位與 運(yùn)算 铺纽,如果結(jié)果不為 0 柬帕,則表示 開(kāi)關(guān)序列 中 包含第一、二個(gè)二態(tài)量中的至少一個(gè)
原理:
根據(jù)上表中 與 定義狡门,可得:1 & 1 == 1
和 0 & 1 == 0
陷寝;
即:任何位 x 和 1 做 與運(yùn)算 的值都與原來(lái)位 x 相等;任何位 x 和 0 做 與運(yùn)算 的值是 0其馏;
而 掩碼中 不需要的二態(tài)量的二進(jìn)制位被置為了 0 凤跑,在與 開(kāi)關(guān)序列 相與 時(shí),不需要的位就被 置為了 0 叛复;掩碼中 需要的二態(tài)量置為 1 仔引,在與 開(kāi)關(guān)序列 相與 時(shí),需要的位的值 就被保留了下來(lái) 褐奥;所以咖耘,開(kāi)關(guān)序列 與 某個(gè)二態(tài)量的 掩碼 做 與運(yùn)算 后,得到的值就是 該二態(tài)量的值撬码;
判斷是否包含某些二態(tài)量中的每一個(gè)
將 開(kāi)關(guān)序列 與 這些二態(tài)量的 掩碼 做 位與 運(yùn)算 儿倒,再讓 運(yùn)算結(jié)果 與 掩碼 相 相等比較,如果相等呜笑,則表示 開(kāi)關(guān)序列 包含這些二態(tài)量中的每一個(gè)义桂;如果不相等 找筝,則表示 開(kāi)關(guān)序列中 并不包含 這些所有的二態(tài)量;
(switchSeq & mask) === mask
示例:
var switchSeq = 0b0110; //包含多個(gè)二態(tài)量入信息的開(kāi)關(guān)序列
var mask = 0b0011; //第一慷吊、二個(gè)二態(tài)量的掩碼
var all = (switchSeq & mask) === mask; //將 開(kāi)關(guān)序列 與 第一袖裕、二個(gè)二態(tài)量的 掩碼 做 位與 運(yùn)算 然后再 與 掩碼 做相等比較,如果相等溉瓶,則表示 開(kāi)關(guān)序列 包含這些二態(tài)量中的每一個(gè)急鳄;如果不相等 ,則表示 開(kāi)關(guān)序列中 并不包含 這些所有的二態(tài)量堰酿;
原理:
根據(jù)上表中 與 定義疾宏,可得:1 & 1 == 1
和 0 & 1 == 0
;
即:任何位 x 和 1 做 與運(yùn)算 的值都與原來(lái)位 x 相等触创;任何位 x 和 0 做 與運(yùn)算 的值是 0坎藐;
而 掩碼中 不需要的二態(tài)量的二進(jìn)制位被置為了 0 ,在與 開(kāi)關(guān)序列 相與 時(shí)哼绑,不需要的位就被 置為了 0 岩馍,這與 掩碼 中的值一樣;掩碼中 需要的二態(tài)量置為 1 抖韩,在與 開(kāi)關(guān)序列 相與 時(shí)蛀恩,需要的位的值 就被保留了下來(lái) ;所以茂浮,如果 開(kāi)關(guān)序列 包含所有這些需要的位双谆,則 掩碼 在與 開(kāi)關(guān)序列 相與 時(shí),需要的位的值 就是 1 席揽,即:與 掩碼 中的值一樣顽馋;所以,如果 開(kāi)關(guān)序列 包含某些二態(tài)量中的每一個(gè)幌羞,則 開(kāi)關(guān)序列 與 這些二態(tài)量的 掩碼 做 位與 運(yùn)算后的值 會(huì)和 掩碼 相等寸谜;
八、求對(duì)2的冪的余數(shù)
JavaScript 語(yǔ)言自帶取余運(yùn)算符 %
新翎,但如果是求 對(duì) 2的整數(shù)次冪 的余數(shù),則還有另一個(gè)更加高效的方法住练;
求 數(shù)x 對(duì) 2 的 n 次冪 的余數(shù)地啰,可用 數(shù) x 與 從右往左連續(xù) n 個(gè)位是 1 的二進(jìn)制數(shù) mask 做位與運(yùn)算 x & mask
,運(yùn)算的結(jié)果就是 所求余數(shù)讲逛;
示例:
求 10 對(duì) 2的3次方(也就是8)的余數(shù)亏吝;
var x = 10;
var mask = 0b111; //因?yàn)橐?對(duì) 2的3次方的冪的余數(shù),所以 mask 為3個(gè)二進(jìn)制1盏混;
var mod = x & mask; // mod 即為 10 對(duì) 2的3次方的冪的 余數(shù)
原理:
將 數(shù) x 與 從右往左連續(xù) n 個(gè)位是 1 的二進(jìn)制數(shù) mask 做 位與 運(yùn)算 x & mask
后蔚鸥,數(shù) x 的 最右邊的 n 位的值就被保留了下來(lái)惜论,其它位都置為了 0 ,此時(shí)的數(shù)值正是小于 2的次方的冪的數(shù)止喷,也就是 對(duì) 2的n次方的余數(shù)馆类;
九、奇偶判斷
判斷一個(gè)整數(shù) x 是奇數(shù)還是偶數(shù)的方法是:求這個(gè)數(shù)對(duì)2的余數(shù) x % 2
弹谁,如果是余 零 乾巧,則該數(shù)為偶數(shù),如果余數(shù)不是零预愤,則該是奇數(shù)沟于;
由于 2 是 2 的 1 次方的冪,所以植康,可以用 求對(duì)2的冪的余數(shù) 中所述的方法旷太,讓該數(shù) 與 1 相 相與 x & 1
,如果結(jié)果是 零销睁,則該數(shù)是偶數(shù)供璧,如果該數(shù)不為 零 ,則該數(shù)是奇數(shù)榄攀;
所以嗜傅,判斷一個(gè)數(shù) x
是奇數(shù)還是偶數(shù)的方法有 2 種:
- 求該數(shù)對(duì) 2 的余數(shù)
x % 2
是否是零;如果是零檩赢,則為偶數(shù)吕嘀; - 讓該數(shù) 和 1 做 位與運(yùn)算
x & 1
兑徘,如果結(jié)果是零酌住,則為偶數(shù);
十失暴、交互兩個(gè)數(shù)
交互兩個(gè)整數(shù)變量 a军浆、b 的方法有以下幾種:
方法1:中間變量
var temp = a; // 定義臨時(shí)變量棕洋,用來(lái)臨時(shí)保存 a 的原始值;
a = b; // 次 b 賦值給 a乒融;
b = temp; // 將 a 的原始值 賦值給 b掰盘;
方法2:表達(dá)式暫存
a = [b, b = a][0];
或
a = {a:b, _:b = a}.a;
這種方式是利用 表達(dá)式 的計(jì)算 順序 將 原始值 暫存 在 數(shù)組的元素 或 對(duì)象的屬性 中 來(lái) 實(shí)現(xiàn) 臨時(shí)變量 的效果;
方法3:異或運(yùn)算
a ^= b;
b ^= a;
a ^= b;
數(shù)學(xué)證明:
上面給出的是代碼赞季;代碼的變量是動(dòng)態(tài)的愧捕,數(shù)學(xué)的變量是靜態(tài)的,為了方便證明申钩,先把方面的代碼改成如下等效形式:
等效代碼:
t = a ^ b;
b = t ^ b;
a = t ^ b;
然后用數(shù)學(xué)靜態(tài)變量的方式來(lái)等效表示上面的等式次绘,即:用 a、b 表示代碼中初始的 a、b 值邮偎;用 x 表示 代碼中最終 a 的值管跺,用 y 表示代碼中最終的 b 的值;
等效的數(shù)學(xué)表達(dá)式:
t = a ^ b;
y = t ^ b;
x = t ^ y;
用數(shù)學(xué)符號(hào)表示:
t = a⊕b
y = t⊕b
x = t⊕y
證明之前禾进,需要選證明一個(gè)公式:
(a⊕b)⊕b = a
即: a 與 b 異或的值 再與 b 異或豁跑,最終的結(jié)果 等于 a ;
(a⊕b)⊕b = a
證明過(guò)程:
所以命迈,就有:
所以:
y = a
x = b
又因?yàn)?x 表示 代碼中最終 a 的值贩绕,y 表示代碼中最終的 b 的值;
所以壶愤,對(duì)于代碼
t = a ^ b;
b = t ^ b;
a = t ^ b;
執(zhí)行完成后淑倾,最終 b 的值 是 原來(lái) a 的值,最終 a 的值征椒,是原來(lái) b 的值娇哆,從而實(shí)現(xiàn)了 變量 a 和 b 的值互換的操作;