成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

由left-pad扯到JS中的位運(yùn)算

LeoHsiun / 1653人閱讀

摘要:原碼補(bǔ)碼和反碼原碼一個(gè)數(shù)在計(jì)算機(jī)中是以二進(jìn)制的形式存在的,其中第一位存放符號(hào)正數(shù)為負(fù)數(shù)為。中的位運(yùn)算在中按位操作符會(huì)將其操作數(shù)轉(zhuǎn)成補(bǔ)碼形式的有符號(hào)位整數(shù)。原文鏈接由扯到中的位運(yùn)算

這個(gè)話題的由來(lái)是2016年3月份的時(shí)候 NPM 社區(qū)發(fā)生了‘left-pad’事件,不久后社區(qū)就有人發(fā)布了用來(lái)補(bǔ)救的,也是現(xiàn)在大家能用到的 left-pad 庫(kù)。

最開(kāi)始這個(gè)庫(kù)的代碼是這樣的。

module.exports = leftpad;
          
function leftpad (str, len, ch) {
  str = String(str);
  
  var i = -1;
  
  if (!ch && ch !== 0) ch = " ";
  
  len = len - str.length;
  
  while (++i < len) {
    str = ch + str;
  }
  
  return str;
}

我第一次看到這段代碼的時(shí)候,沒(méi)看出什么毛病,覺(jué)得清晰明了。后來(lái)刷微博的時(shí)候@左耳朵耗子老師指出了這段代碼可以寫(xiě)得更有效率些,于是他就貼出了自己寫(xiě)的版本并給 left-pad 提了 PR,代碼如下:

module.exports = leftpad;
     
function leftpad (str, len, ch) {
  //convert the `str` to String
  str = str +"";
     
  //needn"t to pad
  len = len - str.length;
  if (len <= 0) return str;
     
  //convert the `ch` to String
  if (!ch && ch !== 0) ch = " ";
  ch = ch + "";
     
  var pad = "";
  while (true) {
    if (len & 1) pad += ch;
    len >>= 1;
    if (len) ch += ch;
    else break;
  }
  return pad + str;
}

我當(dāng)時(shí)看到他的這段代碼的里面的 &>>運(yùn)算符的時(shí)候一下有點(diǎn)懵了,只知道這是位運(yùn)算里面的‘按位與’和‘右移’運(yùn)算,但是完全不知道為什么這樣寫(xiě)就能提高效率。于是就想著去了解位運(yùn)算的實(shí)質(zhì)和使用場(chǎng)景。

在了解位運(yùn)算之前,我們先必須了解一下什么是原碼、反碼和補(bǔ)碼以及二進(jìn)制與十進(jìn)制的轉(zhuǎn)換。

原碼、補(bǔ)碼和反碼 原碼

一個(gè)數(shù)在計(jì)算機(jī)中是以二進(jìn)制的形式存在的,其中第一位存放符號(hào), 正數(shù)為0, 負(fù)數(shù)為1。原碼就是用第一位存放符號(hào)的二進(jìn)制數(shù)值。例如2的原碼為00000010,-2的原碼為10000010。

反碼

正數(shù)的反碼是其本身。負(fù)數(shù)的反碼是在其原碼的基礎(chǔ)上,符號(hào)位不變,其余各位取反,即0變1,1變0。

[+3]=[00000011]原=[00000011]反
[-3]=[10000011]原=[11111100]反

可見(jiàn)如果一個(gè)反碼表示的是負(fù)數(shù),并不能直觀的看出它的數(shù)值,通常要將其轉(zhuǎn)換成原碼再計(jì)算。

補(bǔ)碼

正數(shù)的補(bǔ)碼是其本身。負(fù)數(shù)的補(bǔ)碼是在其原碼的基礎(chǔ)上,符號(hào)位不變,其余各位取反,最后+1。(即負(fù)數(shù)的補(bǔ)碼為在其反碼的基礎(chǔ)上+1)。

[+3]=[00000011]原=[00000011]反=[00000011]補(bǔ)
[-3]=[10000011]原=[11111100]反=[11111101]補(bǔ)

可見(jiàn)對(duì)于負(fù)數(shù),補(bǔ)碼的表示方式也是讓人無(wú)法直觀看出其數(shù)值的,通常也需要轉(zhuǎn)換成原碼再計(jì)算。

二進(jìn)制與十進(jìn)制的轉(zhuǎn)換

二進(jìn)制與十進(jìn)制的區(qū)別在于數(shù)運(yùn)算時(shí)是逢幾進(jìn)一位。二進(jìn)制是逢2進(jìn)一位,十進(jìn)制也就是我們常用的0-9是逢10進(jìn)一位

正整數(shù)的十進(jìn)制轉(zhuǎn)二進(jìn)制

正整數(shù)的十進(jìn)制轉(zhuǎn)二進(jìn)制的方法為將一個(gè)十進(jìn)制數(shù)除以2,得到的商再除以2,以此類(lèi)推直到商等于1或0時(shí)為止,倒取除得的余數(shù),即為轉(zhuǎn)換所得的二進(jìn)制數(shù)的結(jié)果。

例如把52換算成二進(jìn)制數(shù),計(jì)算過(guò)程如下圖:

52除以2得到的余數(shù)依次為:0、0、1、0、1、1,倒序排列,所以52對(duì)應(yīng)的二進(jìn)制數(shù)就是110100。

負(fù)整數(shù)的十進(jìn)制轉(zhuǎn)二進(jìn)制

負(fù)整數(shù)的十進(jìn)制轉(zhuǎn)二進(jìn)制為將該負(fù)整數(shù)對(duì)應(yīng)的正整數(shù)先轉(zhuǎn)換成二進(jìn)制,然后對(duì)其“取反”,再對(duì)取反后的結(jié)果+1。即負(fù)整數(shù)采用其二進(jìn)制補(bǔ)碼的形式存儲(chǔ)。
至于負(fù)數(shù)為什么要用二進(jìn)制補(bǔ)碼的形式存儲(chǔ),可參考一篇阮一峰的文章《關(guān)于2的補(bǔ)碼》。
例如 -52 的原碼為 10110100,其反碼為 11001011,其補(bǔ)碼為 11001100。所以 -52 轉(zhuǎn)換為二進(jìn)制后為 11001100。

十進(jìn)制小數(shù)轉(zhuǎn)二進(jìn)制

十進(jìn)制小數(shù)轉(zhuǎn)二進(jìn)制的方法為“乘2取整”,對(duì)十進(jìn)制小數(shù)乘2得到的整數(shù)部分和小數(shù)部分,整數(shù)部分即是相應(yīng)的二進(jìn)制數(shù)碼,再用2乘小數(shù)部分(之前乘后得到新的小數(shù)部分),又得到整數(shù)和小數(shù)部分。
如此不斷重復(fù),直到小數(shù)部分為0或達(dá)到精度要求為止。第一次所得到為最高位,最后一次得到為最低位。

如:0.25的二進(jìn)制
0.25*2=0.5 取整是0
0.5*2=1.0    取整是1
即0.25的二進(jìn)制為 0.01 ( 第一次所得到為最高位,最后一次得到為最低位)
   
0.8125的二進(jìn)制
0.8125*2=1.625   取整是1
0.625*2=1.25     取整是1
0.25*2=0.5       取整是0
0.5*2=1.0        取整是1
即0.8125的二進(jìn)制是0.1101
二進(jìn)制轉(zhuǎn)十進(jìn)制

從最后一位開(kāi)始算,依次列為第0、1、2...位,第n位的數(shù)(0或1)乘以2的n次方,將得到的結(jié)果相加就是得到的十進(jìn)制數(shù)。
例如二進(jìn)制為110的數(shù),將其轉(zhuǎn)為十進(jìn)制的過(guò)程如下

個(gè)位數(shù) 0 與 2o 相乘:0 × 2o = 0
十位數(shù) 1 與 21 相乘:1 × 21 = 2
百位數(shù) 1 與 22 相乘:1 × 22 = 4
將得到的結(jié)果相加:0+2+4=6
所以二進(jìn)制 110 轉(zhuǎn)換為十進(jìn)制后的數(shù)值為 6。

小數(shù)二進(jìn)制用數(shù)值乘以2的負(fù)冪次然后相加。

JavaScript 中的位運(yùn)算

在 ECMAScript 中按位操作符會(huì)將其操作數(shù)轉(zhuǎn)成補(bǔ)碼形式的有符號(hào)32位整數(shù)。下面是ECMAScript 規(guī)格中對(duì)于位運(yùn)算的執(zhí)行過(guò)程的表述:

The production A : A @ B, where @ is one of the bitwise operators in the productions above, is evaluated as follows:
1. Let lref be the result of evaluating A.
2. Let lval be GetValue(lref).
3. ReturnIfAbrupt(lval).
4. Let rref be the result of evaluating B.
5. Let rval be GetValue(rref).
6. ReturnIfAbrupt(rval).
7. Let lnum be ToInt32(lval).
8. ReturnIfAbrupt(lnum).
9. Let rnum be ToInt32(rval).
10. ReturnIfAbrupt(rnum).
11. Return the result of applying the bitwise operator @ to lnum and rnum. The result is a signed 32 bit integer.

需要注意的是第七步和第九步,根據(jù) ES 的標(biāo)準(zhǔn),超過(guò)32位的整數(shù)會(huì)被截?cái)?,而小?shù)部分則會(huì)被直接舍棄。所以由此可以知道,在 JS 中,當(dāng)位運(yùn)算中有操作數(shù)大于或等于232時(shí),就會(huì)出現(xiàn)意想不到的結(jié)果。

JavaScript 中的位運(yùn)算有:&(按位與)、|(按位或)、~(取反)^(按位異或)、<<(左移)>>(有符號(hào)右移)>>>(無(wú)符號(hào)右移)。

&按位與

對(duì)每一個(gè)比特位執(zhí)行與(AND)操作。只有 a 和 b 都是 1 時(shí),a & b 才是 1。
例如:9(base 10) & 14(base 10) = 1001(base2) & 1110(base 2) = 1000(base 2) = 8(base 10)

因?yàn)楫?dāng)只有 a 和 b 都是 1 時(shí),a&b才等于1,所以任一數(shù)值 x 與0(二進(jìn)制的每一位都是0)按位與操作,其結(jié)果都為0。將任一數(shù)值 x 與 -1(二進(jìn)制的每一位都是1)按位與操作,其結(jié)果都為 x。
利用 & 運(yùn)算的特點(diǎn),我們可以用以簡(jiǎn)單的判斷奇偶數(shù),公式:

(n & 1) === 0 //true 為偶數(shù),false 為奇數(shù)。

因?yàn)?1 的二進(jìn)制只有最后一位為1,其余位都是0,所以其判斷奇偶的實(shí)質(zhì)是判斷二進(jìn)制數(shù)最后一位是 0 還是 1。奇數(shù)的二進(jìn)制最后一位是 1,偶數(shù)是0。

當(dāng)然還可以利用 JS 在做位運(yùn)算時(shí)會(huì)舍棄掉小數(shù)部分的特性來(lái)做向下取整的運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 x&-1=x,所以當(dāng) x 為小數(shù)時(shí)有 x&-1===Math.floor(x)。

|按位或

對(duì)每一個(gè)比特位執(zhí)行或(OR)操作。如果 a 或 b 為 1,則 a | b 結(jié)果為 1。
例如:9(base 10) | 14(base 10) = 1001(base2) | 1110(base 2) = 1111(base 2) = 15(base 10)

因?yàn)橹灰?a 或 b 其中一個(gè)是 1 時(shí),a|b就等于1,所以任一數(shù)值 x 與-1(二進(jìn)制的每一位都是1)按位與操作,其結(jié)果都為-1。將任一數(shù)值 x 與 0(二進(jìn)制的每一位都是0)按位與操作,其結(jié)果都為 x。

同樣,按位或也可以做向下取整運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 x|0=x,所以當(dāng) x 為小數(shù)時(shí)有 x|0===Math.floor(x)。

~取反

對(duì)每一個(gè)比特位執(zhí)行非(NOT)操作。~a 結(jié)果為 a 的反轉(zhuǎn)(即反碼)。

9 (base 10)  = 00000000000000000000000000001001 (base 2)
               --------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

負(fù)數(shù)的二進(jìn)制轉(zhuǎn)化為十進(jìn)制的規(guī)則是,符號(hào)位不變,其他位取反后加 1。

對(duì)任一數(shù)值 x 進(jìn)行按位非操作的結(jié)果為 -(x + 1)。~~x === x。

同樣,取反也可以做向下取整運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 ~~x===x,所以當(dāng) x 為小數(shù)時(shí)有 ~~x===Math.floor(x)。

^按位異或

對(duì)每一對(duì)比特位執(zhí)行異或(XOR)操作。當(dāng) a 和 b 不相同時(shí),a ^ b 的結(jié)果為 1。
例如:9(base 10) ^ 14(base 10) = 1001(base2) ^ 1110(base 2) = 0111(base 2) = 7(base 10)

將任一數(shù)值 x 與 0 進(jìn)行異或操作,其結(jié)果為 x。將任一數(shù)值 x 與 -1 進(jìn)行異或操作,其結(jié)果為 ~x,即 x^-1=~x。
同樣,按位異或也可以做向下取整運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 (x^0)===x,所以當(dāng) x 為小數(shù)時(shí)有 (x^0)===Math.floor(x)。

<<左移運(yùn)算

它把數(shù)字中的所有數(shù)位向左移動(dòng)指定的數(shù)量,向左被移出的位被丟棄,右側(cè)用 0 補(bǔ)充。
例如,把數(shù)字 2(等于二進(jìn)制中的 10)左移 5 位,結(jié)果為 64(等于二進(jìn)制中的 1000000):

var iOld = 2;        //等于二進(jìn)制 10
var iNew = iOld << 5;    //等于二進(jìn)制 1000000 十進(jìn)制 64

因?yàn)槎M(jìn)制10轉(zhuǎn)換成十進(jìn)制的過(guò)程為 1×21+0×2o,在運(yùn)算中2的指數(shù)與位置數(shù)相對(duì)應(yīng),當(dāng)左移五位后就變成了 1×21??+0×2o??= 1×21×2?+0×2o×2? = (1×21+0×2o)×2?。所以由此可以看出當(dāng)2左移五位就變成了 2×2?=64。
所以有一個(gè)數(shù)左移 n 為,即為這個(gè)數(shù)乘以2的n次方。x<。
同樣,左移運(yùn)算也可以做向下取整運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 (x<<0)===x,所以當(dāng) x 為小數(shù)時(shí)有 (x<<0)===Math.floor(x)

>>有符號(hào)右移運(yùn)算

它把 32 位數(shù)字中的所有數(shù)位整體右移,同時(shí)保留該數(shù)的符號(hào)(正號(hào)或負(fù)號(hào))。有符號(hào)右移運(yùn)算符恰好與左移運(yùn)算相反。例如,把 64 右移 5 位,將變?yōu)?2。
因?yàn)橛蟹?hào)右移運(yùn)算符與左移運(yùn)算相反,所以有一個(gè)數(shù)左移 n 為,即為這個(gè)數(shù)除以2的n次方。x<。
同樣,有符號(hào)右移運(yùn)算也可以做向下取整運(yùn)算,因?yàn)楫?dāng) x 為整數(shù)時(shí)有 (x>>0)===x,所以當(dāng) x 為小數(shù)時(shí)有 (x>>0)===Math.floor(x)。

>>>無(wú)符號(hào)右移運(yùn)算

它將無(wú)符號(hào) 32 位數(shù)的所有數(shù)位整體右移。對(duì)于正數(shù),無(wú)符號(hào)右移運(yùn)算的結(jié)果與有符號(hào)右移運(yùn)算一樣,而負(fù)數(shù)則被作為正數(shù)來(lái)處理。

-9 (base 10): 11111111111111111111111111110111 (base 2)
                    --------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

根據(jù)無(wú)符號(hào)右移的正數(shù)右移與有符號(hào)右移運(yùn)算一樣,而負(fù)數(shù)的無(wú)符號(hào)右移一定為非負(fù)的特征,可以用來(lái)判斷數(shù)字的正負(fù),如下:

function isPos(n) {
  return (n === (n >>> 0)) ? true : false;  
}
    
isPos(-1); // false
isPos(1); // true
總結(jié)

根據(jù) JS 的位運(yùn)算,可以得出如下信息:
1、所有的位運(yùn)算都可以對(duì)小數(shù)取底。
2、對(duì)于按位與&,可以用 (n & 1) === 0 //true 為偶數(shù),false 為奇數(shù)。來(lái)判斷奇偶。用x&-1===Math.floor(x)來(lái)向下取底。
3、對(duì)于按位或|,可以用x|0===Math.floor(x)來(lái)向下取底。
4、對(duì)于取反運(yùn)算~,可以用~~x===Math.floor(x)來(lái)向下取底。
5、對(duì)于異或運(yùn)算^,可以用(x^0)===Math.floor(x)來(lái)向下取底。
6、對(duì)于左移運(yùn)算<<,可以x<來(lái)求2的n次方,用x<<0===Math.floor(x)來(lái)向下取底。
7、對(duì)于有符號(hào)右移運(yùn)算>>,可以x<求一個(gè)數(shù)字的 N 等分,用x>>0===Math.floor(x)來(lái)向下取底。
8、對(duì)于無(wú)符號(hào)右移運(yùn)算>>>,可以(n === (n >>> 0)) ? true : false;來(lái)判斷數(shù)字正負(fù),用x>>>0===Math.floor(x)來(lái)向下取底。

用移位運(yùn)算來(lái)替代普通算術(shù)能獲得更高的效率。移位運(yùn)算翻譯成機(jī)器碼的長(zhǎng)度更短,執(zhí)行更快,需要的硬件開(kāi)銷(xiāo)更小。

原文鏈接:由left-pad扯到JS中的位運(yùn)算

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88865.html

相關(guān)文章

  • 深入研究js的位運(yùn)算及用法

    摘要:雖然在內(nèi)部,數(shù)值都是以位浮點(diǎn)數(shù)的形式儲(chǔ)存,但是做位運(yùn)算的時(shí)候,是以位帶符號(hào)的整數(shù)進(jìn)行運(yùn)算的,并且返回值也是一個(gè)位帶符號(hào)的整數(shù)。如下表應(yīng)用場(chǎng)景取整對(duì)于一般的整數(shù),返回值不會(huì)有任何變化。例如,結(jié)果為負(fù)數(shù)存儲(chǔ)采用的形式是二進(jìn)制補(bǔ)碼。 什么是位運(yùn)算? 位運(yùn)算是在數(shù)字底層(即表示數(shù)字的 32 個(gè)數(shù)位)進(jìn)行運(yùn)算的。由于位運(yùn)算是低級(jí)的運(yùn)算操作,所以速度往往也是最快的(相對(duì)其它運(yùn)算如加減乘除來(lái)說(shuō)),并...

    zhoutao 評(píng)論0 收藏0
  • 深入研究js的位運(yùn)算及用法

    摘要:雖然在內(nèi)部,數(shù)值都是以位浮點(diǎn)數(shù)的形式儲(chǔ)存,但是做位運(yùn)算的時(shí)候,是以位帶符號(hào)的整數(shù)進(jìn)行運(yùn)算的,并且返回值也是一個(gè)位帶符號(hào)的整數(shù)。如下表應(yīng)用場(chǎng)景取整對(duì)于一般的整數(shù),返回值不會(huì)有任何變化。例如,結(jié)果為負(fù)數(shù)存儲(chǔ)采用的形式是二進(jìn)制補(bǔ)碼。 什么是位運(yùn)算? 位運(yùn)算是在數(shù)字底層(即表示數(shù)字的 32 個(gè)數(shù)位)進(jìn)行運(yùn)算的。由于位運(yùn)算是低級(jí)的運(yùn)算操作,所以速度往往也是最快的(相對(duì)其它運(yùn)算如加減乘除來(lái)說(shuō)),并...

    hlcfan 評(píng)論0 收藏0
  • 深入講解js的位運(yùn)算及實(shí)際用法

    摘要:雖然在內(nèi)部,數(shù)值都是以位浮點(diǎn)數(shù)的形式儲(chǔ)存,但是做位運(yùn)算的時(shí)候,是以位帶符號(hào)的整數(shù)進(jìn)行運(yùn)算的,并且返回值也是一個(gè)位帶符號(hào)的整數(shù)。如下表應(yīng)用場(chǎng)景取整對(duì)于一般的整數(shù),返回值不會(huì)有任何變化。例如,結(jié)果為負(fù)數(shù)存儲(chǔ)采用的形式是二進(jìn)制補(bǔ)碼。 什么是位運(yùn)算? 位運(yùn)算是在數(shù)字底層(即表示數(shù)字的 32 個(gè)數(shù)位)進(jìn)行運(yùn)算的。由于位運(yùn)算是低級(jí)的運(yùn)算操作,所以速度往往也是最快的(相對(duì)其它運(yùn)算如加減乘除來(lái)說(shuō)),并...

    jifei 評(píng)論0 收藏0
  • JS魔法堂:徹底理解0.1 + 0.2 === 0.30000000000000004的背后

    摘要:也就是說(shuō)不僅是會(huì)產(chǎn)生這種問(wèn)題,只要是采用的浮點(diǎn)數(shù)編碼方式來(lái)表示浮點(diǎn)數(shù)時(shí),則會(huì)產(chǎn)生這類(lèi)問(wèn)題。到這里我們都理解只要采取的浮點(diǎn)數(shù)編碼的語(yǔ)言均會(huì)出現(xiàn)上述問(wèn)題,只是它們的標(biāo)準(zhǔn)類(lèi)庫(kù)已經(jīng)為我們提供了解決方案而已。 Brief 一天有個(gè)朋友問(wèn)我JS中計(jì)算0.7 * 180怎么會(huì)等于125.99999999998,坑也太多了吧!那時(shí)我猜測(cè)是二進(jìn)制表示數(shù)值時(shí)發(fā)生round-off error所導(dǎo)致,但并不...

    JerryWangSAP 評(píng)論0 收藏0
  • String.prototype.repeat在V8和Chakra中的實(shí)現(xiàn)

    摘要:作者源地址最近一個(gè)事件搞得圈沸沸揚(yáng)揚(yáng)的我們暫且把這個(gè)事情放一邊來(lái)看看本身的實(shí)現(xiàn)的源碼如下這段程序的作用是給一個(gè)字符串或可以轉(zhuǎn)成的變量用字符在左邊補(bǔ)位將其補(bǔ)到長(zhǎng)度為當(dāng)然這個(gè)程序沒(méi)做充足的參數(shù)檢查這個(gè)就不細(xì)說(shuō)了我們分析一下它的效率如果 作者: @flowmemo源地址: http://flowmemo.github.io/2016/03/25/str... 最近一個(gè)left-pad事件搞得...

    wushuiyong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<