1. 小數
假設你打算寫一個最簡單的計算機,或任何需要計算的程式(從計算金錢到複雜的物理)。現實世界中很多東西都不是整數,這是改不了的事實!
那就讓我們武裝一個新的資料型別吧!
在程式設計中,小數也稱為實數,或浮點數(floating-point)。在 Java(以及多數語言)中,它們用來儲存不僅是整數,還有「小數」的數值:像是 3.14、-28.57、2.718281828 等等。
實數主要有兩種型別:
| 型別 | 內容 | 值域(約) | 精確度 | 典型大小 |
|---|---|---|---|---|
|
數值 | ±1.5 × 10-45 ... ±3.4 × 1038 | 約 7 位有效數字 | 4 位元組 |
|
數值 | ±5.0 × 10-324 ... ±1.7 × 10308 | 約 15–16 位有效數字 | 8 位元組 |
型別 float
float 的名稱來自 floating-point number——浮點數。數學中的實數有其確定的性質,而電腦有許多限制。因此在 Java 中把小數稱為「實數」並不嚴謹,通常稱為「浮點數」更為恰當。
float 通常可儲存 7 位有效數字(例如 0.1234567)、十的冪次,並在記憶體中占用 4 位元組。對精確計算而言這很有限,因此大家很快就改用雙精度的數值。
型別 double
double 的名稱來自 雙精度(double precision)。它在記憶體中占用 8 位元組(是 float 的兩倍),可儲存最多約 15 位有效數字:0.123456789012345。這對大多數含小數的計算已相當足夠,因此在 Java 中,double 是儲存小數的主要型別。
本講的重點將放在 double:預設情況下,它被建議用於所有「一般」的小數。不過接下來我們也會看看 float 型別。
2. 宣告與初始化 double 型別的變數
跟 int 一樣——只是現在改用 double。
// 宣告變數並指定圓周率的值
double pi = 3.1415926;
// 也可以先宣告不初始化
double averageSalary;
averageSalary = 91234.56;
// 可以做運算!
double pizzaPieces = 8;
double friends = 3;
double piecesPerFriend = pizzaPieces / friends; // 2.666...(不是 2)
語法要點:
- 十進位的小數點使用 句點(3.14)。如果使用逗號——會得到編譯錯誤!
- 嚴格來說,寫成 double d = 3; 也不會報錯——型別會自動轉換(整數會無損地轉成「浮點數」)。
3. 使用 Scanner 輸入與輸出小數
先試著輸出一個小數:
double amount = 42.75;
System.out.println(amount); // 會輸出:42.75
很順利!如果加上文字:
System.out.println("您的帳戶餘額:" + amount + " 歐元。"); // 輸出:您的帳戶餘額:42.75 歐元。
鍵盤輸入
要讀取 double,需要使用 Scanner 類別的專用方法:console.nextDouble()。
Scanner console = new Scanner(System.in);
System.out.println("請輸入室外溫度:");
double temperature = console.nextDouble(); // 直接讀入 double
System.out.println("現在室外:" + temperature + " 度。");
4. 型別 double 實戰:算術運算
所有常見的運算(+、-、*、/)都和 int 一樣可用:
double distance = 100.5;
double time = 2.0;
double speed = distance / time; // 50.25
System.out.println("平均速度:" + speed); // 平均速度:50.25
這就是全部的算術差異。唯一的不同:如果至少有一個運算元是 double,除法的結果一定是小數。
與 int 比較
int a = 5, b = 2;
System.out.println(a / b); // 2(餘數被捨去)
double aa = 5, bb = 2;
System.out.println(aa / bb); // 2.5
5. 使用 double 的常見錯誤與奇妙現象
輸入轉換錯誤
經典情境:使用者輸入 3,14,但程式預期 3.14。在 Java 中,Scanner.nextDouble() 依賴目前的地區設定(Locale):在俄/德等地區設定中逗號可用,在英語地區設定中需要句點。如有需要,請為 Scanner 設定 Locale,或先讀字串再自行解析。
// 在 Locale.US 下,如果輸入「3,14」會出問題
double value = console.nextDouble();
電腦中數值「不精確」的問題
這裡常讓新手有點困惑:
double x = 0.1 + 0.2;
System.out.println(x); // 嗯……0.30000000000000004
恭喜,你遇到了電腦內部表示小數的「魔法」。事實上,許多數值在二進位制中無法被精確表示。對多數應用來說這通常不成問題,但在金融與精密科學中就有一些需要注意的細節。
6. 重要:double 與 int——自動與顯式轉換
有時你把整數和小數相加,或把 int 指派給 double 變數——都不會出錯:
int i = 2;
double d = i; // 一切正常!
System.out.println(d); // 2
double dd = 3.7;
int ii = (int) dd; // 需要顯式把 double 轉成 int!
System.out.println(ii); // 3,小數部分被捨去
這常讓人意外——為什麼轉型後小數部分不見了?因為 int 不能儲存小數(小數點後的部分會永久消失)。
更多關於從 double 到 int 的轉換,以及運算子 (int) 的內容,將在下一堂課討論。
7. 格式化輸出:把 double 漂亮地印出
預設印出 double 時常會帶一堆多餘的位數。可以格式化輸出:
double temp = 23.56789;
System.out.println(temp); // 23.56789
// 小數點後 2 位
System.out.println(String.format("%.2f", temp)); // 23.57
// 小數點後 1 位
System.out.println(String.format("%.1f%n", temp)); // 23.6
| 格式 | 結果 | 說明 |
|---|---|---|
|
23.57 | 小數點後 2 位 |
|
23.6 | 小數點後 1 位 |
8. 使用 float 與 double 的常見錯誤
錯誤 №1:將 double 隱式轉換為 float
float f = 1.23; // 錯誤!
編譯器會不高興:「你正試圖把 double 放進 float——這可能導致精度損失!」請務必加上後綴 f。
錯誤 №2:忘了兩個 int 相除會得到 int 結果
int a = 7, b = 2;
double result = a / b; // 3.0,而不是 3.5
要得到小數,請顯式轉型至少一個運算元:
double result = (double) a / b; // 3.5
錯誤 №3:比較小數
不要用 == 來比較小數是否相等。請使用帶有小容差(epsilon)的比較。
錯誤 №4:float 的精度損失
不要把很大的數或需要非常精確的值存進 float——它們可能「壞掉」或丟失重要位數。
GO TO FULL VERSION