3.7運算子
數學運算是計算機的基本用途之一,Java提供了非常豐富的運算子來支援。我們根據運算的特點和性質,把運算子劃分為幾組:基本算數運算子、自增自減運算子、關係運算子、位運算子、邏輯運算子、賦值運算子、其他運算子。下面分別介紹。
3.7.1基本算數運算子
在Java中,採用+、-、*、/、%來表示加、減、乘、除、取餘(取模),這種運算小學就學過,無需多講,列表舉例如下:
運算 |
算式 |
結果(假設a=15,b=10) |
加法 |
a+b |
25 |
減法 |
a-b |
5 |
乘法 |
a*b |
150 |
除法 |
a/b |
1 |
取餘 |
a%b |
5 |
運算非常簡單,但是還是有一些問題需要注意,下面分別用例項來說明。
3.7.1.1型別變化
我們看一段程式碼:
1 public static void main(String[] args) { 2 int a1 = 15; 3 double a2 = 15; 4 int b = 2; 5 int c = 0; 6 System.out.println("整數運算:"); 7 System.out.println("a1 + b = " + (a1 + b)); 8 System.out.println("a1 - b = " + (a1 - b)); 9 System.out.println("a1 * b = " + (a1 * b)); 10 System.out.println("a1 / b = " + (a1 / b)); 11 System.out.println("a1 % b = " + (a1 % b)); 12 System.out.println("浮點數運算:"); 13 System.out.println("a2 + b = " + (a2 + b)); 14 System.out.println("a2 - b = " + (a2 - b)); 15 System.out.println("a2 * b = " + (a2 * b)); 16 System.out.println("a2 / b = " + (a2 / b)); 17 System.out.println("a2 % b = " + (a2 % b)); 18 }
執行結果為:
整數運算: a1 + b = 17 a1 - b = 13 a1 * b = 30 a1 / b = 7 a1 % b = 1 浮點數運算: a2 + b = 17.0 a2 - b = 13.0 a2 * b = 30.0 a2 / b = 7.5 a2 % b = 1.0
我們看到,整數15/2=7,而浮點數15/7=7.5。在Java中,參與運算的2個數有浮點數時,就會自動將非浮點數變成浮點數來運算。
下面為了節省篇幅,就不再分別列出程式碼和結果了。
3.7.1.2被0除問題
0.0 / 0 = NaN 1.0 / 0 = Infinity -1.0 / 0 = -Infinity 1 / 0 = Exception in thread "main" java.lang.ArithmeticException: / by zero at ch03.JibenYunsuanfu.main(JibenYunsuanfu.java:16)
我們看到,浮點數0除以0,得到NaN;正負浮點數除以0得到正負無窮大;整數除以0會丟擲異常。
3.7.1.3原碼反碼補碼
我們知道,Java的整型和浮點型都是有範圍的,如果運算結果超過範圍怎麼辦呢?我們知道int型的最大值是214783647,假如我們+1會得到什麼結果呢?結果為:
2147483647 + 1 = -2147483648
說明這個問題原因之前,得先學習原碼、反碼、補碼的相關知識。
3.7.1.3.1原碼
我們現實生活當中,可以用正負號來表示正負數,但是計算機中只有0和1,怎麼表示正負數呢?於是想出了一個辦法,對於固定字長n的二進位制數,把2n個數劃分為正負數,把最高位規定為符號位,0代表正,1代表負,剩下的二進位制數對應十進位制數的絕對值。例如假設字長為3,那麼一共表示8個數:
十進位制 |
二進位制 |
3 |
011 |
2 |
010 |
1 |
001 |
0 |
000 |
-0 |
100 |
-1 |
101 |
-2 |
110 |
-3 |
111 |
這種規定叫做“原碼”,即3的原碼是011,-3的原碼是111。看起來很完美吧,但是有2個問題:
- 0的表示不唯一
- 無法將減法轉換為加法
0的表示不唯一一目瞭然,為什麼不能將減法轉換為加法?我們看個例子:
2 - 1 = 2 + (-1) = 010 + 101 = 111 = -3(正確結果為1)
結果錯誤。那麼又為什麼要把減法轉換為加法呢?我們學習過計算機組成,知道CPU中只有加法暫存器,因為計算機中處理加法比較簡單,如果要直接處理減法,需要增加邏輯部件,而且處理減法有借位問題很麻煩。因此在計算機中用原碼來進行運算和儲存行不通。
3.7.1.3.2反碼
還有別的辦法嗎?人們又發明了“反碼”。反碼規定:正數的反碼和原碼一致,負數的反碼為該數對應的絕對值的原碼按位取反。假設字長為3,原碼反碼分別如下:
十進位制 |
原碼 |
反碼 |
3 |
011 |
011 |
2 |
010 |
010 |
1 |
001 |
001 |
0 |
000 |
000 |
-0 |
100 |
111 |
-1 |
101 |
110 |
-2 |
110 |
101 |
-3 |
111 |
100 |
反碼解決了減法轉換為加法的問題,但是額外需要多一個規定,就是當發生溢位時,需要對最低位加1。我們看2個例子:
1 – 1 = 1 + (-1) = 001 + 110 = 111 =-0 2 - 1 = 2 + (-1) = 010 + 110 = 1000,溢位了,去掉溢位位後需再加1即000 + 001 = 001 = 1
我們看到,結果都正確。但是還是存在2個問題:
- 0的表示不唯一
- 減法轉加法,需要判斷溢位問題
3.7.1.3.3補碼
繼續探討,於是出現“補碼”。補碼規定正數的補碼和原碼一致,負數的補碼為該數對應的絕對值按位取反後加1(如果溢位丟棄最高位)
十進位制 |
原碼 |
反碼 |
補碼 |
3 |
011 |
011 |
011 |
2 |
010 |
010 |
010 |
1 |
001 |
001 |
001 |
0 |
000 |
000 |
000 |
-0 |
100 |
111 |
000 |
-1 |
101 |
110 |
111 |
-2 |
110 |
101 |
110 |
-3 |
111 |
100 |
101 |
我們發現0的表示唯一了。另外用補碼計算減法也很簡單了,直接轉換即可(溢位直接丟棄最高位),我們看2個例子:
1 – 1 = 1 + (-1) = 001 + 111 = 1000 = 000 = 0 2 - 1 = 2 + (-1) = 010 + 111 = 1001 = 001 = 1
喜歡鑽牛角尖的同學就會問了,為什麼使用補碼就可以解決這些問題呢?有什麼道理嗎?我就知道你會問,還好我也惡補了這段知識,下面我們來研究一下。
3.7.1.3.4補碼原理
我們知道,對於一個3位的二進位制,對應的十進位制為0-7,一共8個。7+1=111+000=1000,去掉溢位位,又變成000即0。我們可以說這8個數字形成了一個閉環。這其實對應數學中的一個概念:模。
模是指一個計量系統的計數範圍,例如我們熟悉的時鐘,它的計數範圍是0-11,模是12。計算機也可以看成一個計量機器,因為計算機的字長是定長的,即儲存和處理的位數是有限的,因此它也有一個計量範圍,即都存在一個“模”。對於字長3位的機器來說,計數範圍是0-7,模是8。“模”實質上是計量器產生“溢位”的量,它的值在計量器上表示不出來,計量器上只能表示出模的餘數。任何有模的計量器,均可化減法為加法運算。
我們以時鐘為例:當前時間是2點,逆時針撥2格變成0點。順時針撥10格也是0點。假設逆時針叫減,順時針叫加,那麼對於模12的系統裡,減2和加10的效果一樣。事實上,減3和加9,減4和加8效果也一樣。我們把2和10、3和9、4和8互稱為補數,特點就是二者相加等於模。因此在有模的系統裡,減去一個數,可以變成加上它的補數,即可以把減法變成加法。
回到3位數的二進位制如下圖:
我們很容易就知道模為8,1和7、2和6、3和5、4和4他們互為補數。列一個表:
減數 |
補數 |
1 |
7 |
2 |
6 |
3 |
5 |
4 |
4 |
5 |
3 |
6 |
2 |
7 |
1 |
但是問題來了,3位二進位制系統裡,雖然減n可以變成加n補,但是由於沒有負數,因此計算減法,需要先計算減數的補數,例如減1,需要計算1的補數8-1。怎麼辦?聰明的你一定可以想到,補數都是成對的,我們把成對的補數中的一半規定為負數是不是就可以了?例如a-1=a+(-1)=a+7,假如我們規定7的二進位制111代表-1,那麼在計算的時候就沒有減法了。同理我們還可以規定110代表-2,101代表-3。至於100是代表4還是-4,都可以,一般我們選擇代表-4。這樣一來,對於3位二進位制系統,表示數的範圍就變成-4~3,而所有的減法就變成加法了。而且這樣一來我們還驚奇的發現:
- 所有的正數最高位都是0,負數最高位都是1
- 所有負數的二進位制都是它所對應的絕對值的二進位制按位取反後+1,就是補碼
到此為止,我們就搞清楚了為什麼在計算中要用補碼來表示負數了。
最後,我們回到開頭的例子:
2147483647 + 1 = -2147483648
現在回答這個問題太easy了。在Java中,一個數字如果不加字尾,預設就是int型的。我們知道int型佔用4個位元組,則int的系統是一個模為232的系統。然後採用補碼規則儲存,這樣最大的正數是231-1=2147483647。這個數再加1就變成231。231的補數是它自己,但是由於231的二進位制最高位是1,我們習慣把它規定為負數,即-231,因此就是-2147483648。