javascript 中的二進制運算


1、原碼、反碼、補碼,正數減法轉補碼加法

js 在進行二進制運算時,使用 32 位二進制整數,由於 js 的整數都是有符號數,最高位0表示正數,1表示負數,因此,js 二進制運算中使用的整數表達范圍是

-Math.pow(2,31) ~ Math.pow(2,31)-1   // -2147483648 ~ 2147483647

 

原碼:最高位 0 表示正,1表示負,其余 31 位是該數的絕對值(真值的絕對值)的二進制形式

反碼:正數反碼與原碼相同,負數反碼是原碼符號位不變,其余31位取反(0變1,1變0)

補碼:正數補碼與原碼相同,負數補碼為反碼加 1 (符號位參與運算,其實只有求 -0 的補碼才涉及最高位進位,因此不用擔心在反碼加1時由於符號位參與運算進位而使 - 變 +)。

 

+0 的反碼:32個0 ,按正數處理,原碼、反碼、補碼都是0。

-0 的反碼:最高位1,其余位由 +0 原碼取反,得到 32 個 1

-0 的補碼:其反碼是 32 個 1 加 1,最高位溢出被舍棄,得到 32 個0

因此,正負 0 的補碼都是 0.

 

由負數的補碼求他的絕對值補碼:負二進制數的絕對值,只要各位(包括符號位)取反,再加1,就得到其絕對值。 

計算機在處理加減運算時,使用補碼進行運算,減法被視為加上一個負數,在處理負數時,用負數的補碼進行加法可以即可得到正確運算結果,補碼是為了統一加減運算而生的。

正數減法轉補碼加法的原理是 32 位數溢出:

對於32 位二進制正整數來說,其模為 

Math.pow(2,32) = 4294967296 

 

32 位正整數最大表達范圍是  4294967296 - 1 ,達到 4294967296 這個值就要進位到33位,33 位是溢出位被丟棄,只得到32 個0(這個道理跟表盤上 0 點 和12 點的時針指在同一個位置是一樣的,表盤以 12 為模),因此,一個數逐漸增大,一旦超出 4294967296-1 的數 M 就可以表示為  M%4294967296

而負數 -M (M為絕對值)可以表示為一個正數:  4294967296 - M這個正數就是負數的補碼對應的二進制正整數,負數的補碼按32位二進制數,與他的原碼相加剛好等於模 ),道理跟表盤一樣,11點和負1點指在同一個位置。

以 -3 為例:

(Array(32).join("0")+(3).toString(2)).slice(-32); // |-3| 的二進制數
原碼 = 10000000000000000000000000000011;
反碼 = 11111111111111111111111111111100; //原碼符號位為1,其余位取反 補碼 = 11111111111111111111111111111101; //反碼加1

那么,有
補碼+原碼 = (反碼+1)+原碼
= (反碼+原碼)+1
= 1+(32位全是 1 的二進制數) //因為反碼由正數形式的原碼的低31位取反加上符號位1得到,因此這兩個數的和的低31位全都是1,加上反碼符號位1,得到32 個1
= Math.pow(2,32)
= 4294967296 //這正是32位二進制數的模, 跟 |-1|+11 = 12 原理一樣

 

正數1 - 正數2 = 正數1 + (-正數2) = 正數1 + (模-正數2) = 正數1 + 正數3  //正數3 = (模-正數2)
這就是: 正數減法 -> 負數加法 -> 補碼加法 的過程。

 

2、位運算

因為 js 的整數默認是帶符號正數,所以在為運算中,只能使用 31 位(0~2147483647),開發者是不能訪問最高位的。

位運算只發生在整數上,因此一個非浮點數參與位運算之前會被向下取整。

為了避免訪問符號位, javascript 在現實 負數的 二進制時,轉換為 符號及  其絕對值的二進制,如:

(-123).toString(2) ;// "-1111011"

 

按位取反(~): 一元運算, 1 變0,0變1 ,如 

         ~123 ; //-124

可以驗證一下這個過程:正數取反,符號位為負,所以結果是一個負數,根據 Math.pow(2,32) - M 可以表示成  -M,可以按下面方法計算

      

 parseInt((Array(32).join(0)+ (123).toString(2)).slice(-32).replace(/\d/g,function(v) {
return (v*1+1)%2;
}),2)-Math.pow(2,32)  // -124 ,如果是負數減 Math.pow(2,32)

需要注意的是, javascript 位運算都是有符號的,因此達到 32 位,其最高位將作為 符號位,取反時應得到正數(取模 Math.pow(2,32) 的補數--兩個數相加得到模,稱這兩個數互為補數)。

 

 

 

按位與(&):兩個數的相同位,都是 1 返回1 ,否則返回0

  123&234 = 106

    123  "00000000000000000000000001111011" 
    234  "00000000000000000000000011101010"
---------------------------------------------
    106  "00000000000000000000000001101010"
  

 

 

按位或(|):兩個數的相同位,有一個是 1 則返回 1 ,否則返回 0

  123|234 = 251

    123  "00000000000000000000000001111011" 
    234  "00000000000000000000000011101010" 
---------------------------------------------
    251  "00000000000000000000000011111011"

 

 

按位異或(^):兩個數的相同位,一個是 1 另一個是 0 則返回 1,否則返回0

123^234 = 145

    123  "00000000000000000000000001111011" 
    234  "00000000000000000000000011101010" 
---------------------------------------------
    145  "00000000000000000000000010010001"

 

 

異或運算的一些特性:

 a ^ a = 0  
 a ^ b = b ^ a  
 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c
 a ^ b ^ a = b
 d = a ^ b ^ c   可以推出  a = d ^ b ^ c //這個特性被用在一些加密算法中

 

//如果運算數在適當范圍內(不溢出), 如 a=1,b=2,交換兩個變量的值

a = [b,b=a][0]

//或者 

a = a^b
b = a^b
a= a^b 

 

利用位的異或運算使用一個數字記錄多個信息:
有幾個狀態值分別是 1、2、8、16 .....

這 些值的規律是,他們的二進制只有一位是 1 ,其余都是 0, 因此, 他們中的任意幾個的按位異或運算的結果都不會出現 兩個數的某一位都是 1 的情 況,並且運算的值都是唯一確定的,也就是,知道運算的結果,就知道是哪幾個數的組合,這樣可以用一個數字記錄多個信息。

1     00000001

2     00000010

4     00000100

8     00001000

16    00010000

1^2^4 = 7  // "00000111"

因此,如果我們知道結果是 7 ,就知道他們是由 1 、2、4 組合而成。

如果我們要設置一個參數,使其包含幾個狀態值,就可以用 按位或運算,

這樣的例子可以參考 PHP 中關於圖片類型的幾個常量,和  PHP 錯誤等級定義的幾個常量。

 

這樣的例子,也許有用十進制數來描述的,比如: 個位數的數字表示某個屬性的狀態,十位數的數字表示另一個屬性的狀態,這樣的話,每個狀態可以有 10 個值,只用一個數字就可以描述的組合非常多。

 

左移位(<<) : 一個數的二進制所有位向左移動,符號位不動,高位溢出丟棄,低位補 0 

      如果不溢出, 左移位的效果是乘以 2。

 

右移位(>>): 一個數的二進制所有位向右移動,符號位不動,高位補0,低位丟棄 

     右移位操作的效果是除以 2 並向下取整。

 

帶符號右移(>>>):移位時符號位跟隨移動,符號位也作為數值看待,所以,該操作的結果是 32 位無符號整數,因此負數的帶符號右移將產生正整數,正數的帶符號右移與 無符號右移相同,這是唯一可以操作符號位的運算。

-123>>>1 ;//2147483586

 

一些要注意的地方:

位運算必須是整數,如果運算元不是可用的整數,將取 0 作為運算元

~NaN; // 將執行 ~0  ,結果為 -1

~'x'; // -1

'hello'|0; // 0
({})|0 ; //0
~Infinity; //-1 同 ~0

 

位移運算不能移動超過31位,如果試圖移動超過31位,將位數 對32取模后再移位

123>>32 //實際是 123>>0  (32%32 = 0)

123>>33 //實際是 123>>1

 

 32位帶符號整數表達范圍是 -Math.pow(2,31) ~ Math.pow(2,31)-1 即 -2147483648~2147483647,而 js 數字的精度是雙精度,64位,如果一個超過 2147483647 的整數參與位運算的時候就需要注意,其二進制溢出了,截取32位后,如果第32位是1將被解讀為負數(補碼)。

2147483648>>0; //-2147483648

4294967296>>0; //0

4294967295>>0; //-1

4294967297>>0; //1


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM