JS位運(yùn)算符的妙用與原理

很多高級(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
  • 六麻掸、與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 == 10 | 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 == 10 & 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 == 10 ^ 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 == 10 & 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 == 10 & 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 == 10 & 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ò)程:

異或性質(zhì)推導(dǎo)

所以命迈,就有:

交換兩個(gè)數(shù)的證明

所以:

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 的值互換的操作;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末勃救,一起剝皮案震驚了整個(gè)濱河市碍讨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒙秒,老刑警劉巖勃黍,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異晕讲,居然都是意外死亡覆获,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)瓢省,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)弄息,“玉大人,你說(shuō)我怎么就攤上這事勤婚∧×浚” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵馒胆,是天一觀的道長(zhǎng)缨称。 經(jīng)常有香客問(wèn)我,道長(zhǎng)祝迂,這世上最難降的妖魔是什么睦尽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮液兽,結(jié)果婚禮上骂删,老公的妹妹穿的比我還像新娘。我一直安慰自己四啰,他們只是感情好宁玫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著柑晒,像睡著了一般欧瘪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匙赞,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天佛掖,我揣著相機(jī)與錄音,去河邊找鬼涌庭。 笑死芥被,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的坐榆。 我是一名探鬼主播拴魄,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼席镀!你這毒婦竟也來(lái)了匹中?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤豪诲,失蹤者是張志新(化名)和其女友劉穎顶捷,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體屎篱,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡服赎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了芳室。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片专肪。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖堪侯,靈堂內(nèi)的尸體忽然破棺而出嚎尤,到底是詐尸還是另有隱情,我是刑警寧澤伍宦,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布芽死,位于F島的核電站,受9級(jí)特大地震影響次洼,放射性物質(zhì)發(fā)生泄漏关贵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一卖毁、第九天 我趴在偏房一處隱蔽的房頂上張望揖曾。 院中可真熱鬧落萎,春花似錦、人聲如沸炭剪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)奴拦。三九已至媒鼓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間错妖,已是汗流浹背绿鸣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暂氯,地道東北人潮模。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像痴施,于是被迫代替她去往敵國(guó)和親再登。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354