1. 程式人生 > >Java程式設計基礎--運算子/原碼反碼補碼【知識體系構建系列】

Java程式設計基礎--運算子/原碼反碼補碼【知識體系構建系列】

運算子一覽

算術運算子

+ 加法 - 相加運算子兩側的值
- 減法 - 左運算元減去右運算元
* 乘法 - 相乘操作符兩側的值
/ 除法 - 左運算元除以右運算元
% 取模 - 右運算元除左運算元的餘數
++ 自增 - 運算元的值增加1
– 自減 - 運算元的值減少1

關係運算符

== 檢查如果兩個運算元的值是否相等,如果相等則條件為真
!= 檢查如果兩個運算元的值是否相等,如果值不相等則條件為真
> 檢查左運算元的值是否大於右運算元的值,如果是那麼條件為真
< 檢查左運算元的值是否小於右運算元的值,如果是那麼條件為真
>= 檢查左運算元的值是否大於或等於右運算元的值,如果是那麼條件為真
<= 檢查左運算元的值是否小於或等於右運算元的值,如果是那麼條件為真

位運算子

& 按位與操作符,當且僅當兩個運算元的某一位都非0時候結果的該位才為1
| 按位或操作符,只要兩個運算元的某一位有一個非0時候結果的該位就為1
^ 按位異或操作符,兩個運算元的某一位不相同時候結果的該位就為1
~ 按位補運算子翻轉運算元的每一位
<< 按位左移運算子。左運算元按位左移右運算元指定的位數
>> 按位右移運算子。左運算元按位右移右運算元指定的位數
>>> 按位右移補零操作符。左運算元的值按右運算元指定的位數右移,移動得到的空位以零填充

邏輯運算子

&& 稱為邏輯與運算子。當且僅當兩個運算元都為真,條件才為真
| | 稱為邏輯或操作符。如果任何兩個運算元任何一個為真,條件為真
! 稱為邏輯非運算子。用來反轉運算元的邏輯狀態。如果條件為true,則邏輯非運算子將得到false。

賦值運算子

= 簡單的賦值運算子,將右運算元的值賦給左側運算元
+= 加和賦值操作符,它把左運算元和右運算元相加賦值給左運算元
-= 減和賦值操作符,它把左運算元和右運算元相減賦值給左運算元
*= 乘和賦值操作符,它把左運算元和右運算元相乘賦值給左運算元
/= 除和賦值操作符,它把左運算元和右運算元相除賦值給左運算元
%= 取模和賦值操作符,它把左運算元和右運算元取模後賦值給左運算元
<<= 左移位賦值運算子
>>= 右移位賦值運算子
&= 按位與賦值運算子
^= 按位異或賦值操作符
|= 按位或賦值操作符

其它運算子

條件運算子(?:)
instanceOf

原碼反碼補碼

各自的概念

原碼就是符號位加上真值的絕對值, 即用第一位表示符號, 其餘位表示值. 比如如果是8位二進位制:

[+1]原 = 0000 0001
[-1]原 = 1000 0001

第一位是符號位. 因為第一位是符號位, 所以8位二進位制數的取值範圍就是:
[1111 1111 , 0111 1111] 即[-127 , 127]
原碼是人腦最容易理解和計算的表示方式.

反碼的表示方法是:
正數的反碼是其本身
負數的反碼是在其原碼的基礎上, 符號位不變,其餘各個位取反.

[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反

可見如果一個反碼錶示的是負數, 人腦無法直觀的看出來它的數值. 通常要將其轉換成原碼再計算.

補碼的表示方法是:
正數的補碼就是其本身
負數的補碼是在其原碼的基礎上, 符號位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]補
[-1] = [10000001]原 = [11111110]反 = [11111111]補

對於負數, 補碼錶示方式也是人腦無法直觀看出其數值的. 通常也需要轉換成原碼在計算其數值.

為什麼計算機用補碼來計算?

對於正數來說:[+1] = [00000001]原 = [00000001]反 = [00000001]補
對於負數來說:[-1] = [10000001]原 = [11111110]反 = [11111111]補

計算機辨別”符號位”顯然會讓計算機的基礎電路設計變得十分複雜! 於是人們想出了將符號位也參與運算的方法. 我們知道, 根據運演算法則減去一個正數等於加上一個負數, 即: 1-1 = 1 + (-1) = 0 , 所以機器可以只有加法而沒有減法, 這樣計算機運算的設計就更簡單了.

原碼:1-1=0,1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2,如果用原碼且讓符號位加入計算,結果是不正確的

反碼:1-1=0,1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0, 發現用反碼計算減法, 結果的真值部分是正確的. 而唯一的問題其實就出現在”0”這個特殊的數值上. 雖然人們理解上+0和-0是一樣的, 但是0帶符號是沒有任何意義的. 而且會有[0000 0000]原和[1000 0000]原兩個編碼表示0.

補碼,補碼的出現, 解決了0的符號以及兩個編碼的問題:1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]補 + [1111 1111]補 = [0000 0000]補=[0000 0000]原
使用補碼, 不僅僅修復了0的符號以及存在兩個編碼的問題, 而且還能夠多表示一個最低數. 這就是為什麼8位二進位制, 使用原碼或反碼錶示的範圍為[-127, +127], 而使用補碼錶示的範圍為[-128, 127].

因為機器使用補碼, 所以對於程式設計中常用到的32位int型別,比如Java, 可以表示範圍是: [-2^31, 2^31-1] 因為第一位表示的是符號位.而使用補碼錶示時又可以多儲存一個最小值.

題目

請寫出下面幾個表示式的結果,答案可以用10進位制或16進制書寫
1. 0xaa | 0x55
2. 15 & 240
3. 10 ^ 12
4. -2 >> 1
5. -2 >>> 1

解答:

  1. 分析:十六進位制數用0x……來表示,後面一個十六進位制位是四位,兩個十六進位制位為一個位元組,最多後面可以有8個十六進位制位,32個位元組,如:0xFFFFFFFF。 或(“ | ”)運算,全0為0,其他為1。
    所以:0xaa 用二進位制表示為 10101010 ,0x55 用二進位制表示為 01010101 ,按位或之後為 11111111 ,十進位制數為255,十六進位制數為 0xFF 。

  2. 分析:10進位制轉換成2進位制,用該數字除以2,記錄商和餘數,利用商再次除以2,記錄商和餘數……直到上為0或餘數為0停止,餘數逆序組成二進位制的從低到高位(最後的餘數為二進位制最低位)。與(“ & ”)運算,全1為1,其他為0 。
    所以: 15 等於1111 ,240等於 11110000,15前面用0補齊為00001111 ,按位與之後為 00000000 ,即結果為0

  3. 分析: 亦或(“ ^ ”)運算,相同取0,不同取1 。
    所以:1010 ^ 1100 =0110 , 十進位制表示為6,十六進位制表示為 0x06 。

  4. 分析: 帶符號右移(“ >> ”),即有符號位時,負數符號位補1,正數符號位補0, -2 的二進位制求法是正數取反加1,因此 2 的二進位制表示為0000 0000 0000 0000 0000 0000 0000 0010 ,取反加一為
    1111 1111 1111 1111 1111 1111 1111 1110 ,即 -2 的二進位制表示。
    注: >> , << , >>> , 運算子只針對int型和long型,byte ,short ,char型需要轉換成Int型在進行操作。
    所以: 帶符號右移之後為 1111 1111 1111 1111 1111 1111 1111 1111 ,除符號位之外,減一取反,得到帶符號十進 制數為 -1 。

  5. 分析:無符號右移 (“ >>> ”) ,即無論正負數,右移之後符號位均補 0 。
    所以: -2 的二進位制無符號右移一位之後為 0111 1111 1111 1111 1111 1111 1111 1111,即 2^31 - 1,二的三十一次方減一。
    注:右移和無符號右移主要區別就在於左面最高位補 0 還是補 1 的問題,無符號右移任何時候最高位都補 0 , 有符號右移則是正數補 0 ,負數補 1 。(沒有無符號左移!)