Hộp đồ nghề gồm các phương thức static
java.lang.Math là một lớp chứa sẵn rất nhiều hàm toán học: lũy thừa, căn, giá trị tuyệt đối, làm tròn, lượng giác, logarit và số ngẫu nhiên. Có hai điều khiến nó dễ dùng:
- Nó nằm trong
java.lang, package mà mọi tệp Java tự động import. Bạn không phải viết dòngimportnào. - Mọi phương thức đều là
static. Bạn gọi chúng qua tên lớp,Math.sqrt(25), và không bao giờ tạo đối tượngMath. Trang thành viên static giải thíchstaticnghĩa là gì.
Để ý rằng có kết quả in ra kèm .0, có kết quả thì không. Mỗi phương thức của Math có một kiểu trả về cố định, và nắm được kiểu đó là phần lớn những gì cần học về lớp này.
| Phương thức | Chức năng | Trả về |
|---|---|---|
Math.pow(a, b) | a mũ b | double |
Math.sqrt(x) | căn bậc hai | double |
Math.cbrt(x) | căn bậc ba | double |
Math.abs(x) | giá trị tuyệt đối | cùng kiểu với x |
Math.max(a, b) / Math.min(a, b) | số lớn hơn / nhỏ hơn trong hai số | kiểu rộng hơn trong hai kiểu |
Math.round(x) | số nguyên gần nhất | long với double, int với float |
Math.floor(x) / Math.ceil(x) | làm tròn xuống / làm tròn lên | double |
Math.random() | số ngẫu nhiên từ 0,0 đến dưới 1,0 | double |
Math.PI, Math.E | các hằng số π và e | double |
Math.pow: lũy thừa
Math.pow(base, exponent) nâng một số lên lũy thừa. Nó nhận hai tham số double và trả về một double, kể cả khi bạn truyền vào số nguyên:
Để lưu kết quả vào một int, hãy ép kiểu bằng (int). Với lũy thừa nhỏ của số nguyên, kết quả double là chính xác, nên ép kiểu cho ra đáp án đúng. Kết quả lớn mới là chỗ ép kiểu bị sai: một double lớn hơn Integer.MAX_VALUE (2.147.483.647) khi ép kiểu sẽ cho đúng bằng Integer.MAX_VALUE, nên (int) Math.pow(2, 40) cho ra 2147483647. Hãy ép sang long cho các lũy thừa lớn hơn, và nhớ rằng trên 2⁵³ thì double không biểu diễn chính xác được mọi số nguyên. Trang ép kiểu giải thích việc ép kiểu làm gì với giá trị.
Java không có toán tử lũy thừa. 2 ^ 3 vẫn biên dịch được, nhưng ^ là phép XOR theo bit, và kết quả là 1, không phải 8. Đây là một trong những bug phổ biến nhất của người mới khi viết code tính toán trong Java.
Một double từ mười triệu trở lên sẽ được in ở dạng ký hiệu khoa học, điều khiến nhiều người bất ngờ khi lần đầu in một lũy thừa của mười:
1.0E7 nghĩa là 1,0 × 10⁷. Giá trị vẫn như cũ, chỉ cách in ra là khác. Hãy ép sang long, hoặc định dạng bằng String.format("%.0f", value), để in ra dạng chữ số bình thường.
Math.sqrt và Math.cbrt: căn
Math.sqrt(x) trả về căn bậc hai của x dưới dạng double. Math.cbrt(x) trả về căn bậc ba.
Căn bậc hai của một số âm không phải là số thực, nên Math.sqrt(-4) trả về NaN ("not a number", không phải là số) thay vì ném ngoại lệ. NaN lan sang mọi phép tính mà nó chạm vào, vì vậy hãy kiểm tra đầu vào trước khi lấy căn nếu có thể gặp giá trị âm. Math.hypot(a, b) tính trực tiếp sqrt(a*a + b*b), một cách thường dùng để tính khoảng cách giữa hai điểm.
Math.abs: giá trị tuyệt đối
Math.abs(x) bỏ dấu của số. Khác với pow và sqrt, nó trả về đúng kiểu bạn truyền vào: int với int, double với double.
Cách dùng thường gặp là tính một hiệu số khi bạn chỉ quan tâm độ lớn, không quan tâm chiều, như khoảng cách giữa hai vị trí.
Math.max và Math.min
Math.max(a, b) trả về giá trị lớn hơn trong hai giá trị, còn Math.min(a, b) trả về giá trị nhỏ hơn. Chúng nhận đúng hai đối số. Với ba giá trị trở lên, hãy lồng các lời gọi:
Khi hai đối số có kiểu khác nhau, kiểu hẹp hơn được mở rộng trước. Math.max(3, 7.5) so sánh hai giá trị double và trả về một double. Ví dụ cuối giới hạn một giá trị trong khoảng từ 0 đến 100: Math.min chặn nó ở mức 100, Math.max giữ nó không xuống dưới 0.
Làm tròn: round, floor, ceil
Java có ba phương thức làm tròn, khác nhau cả về hướng làm tròn lẫn kiểu trả về:
Math.roundlàm tròn tới số nguyên gần nhất. Giá trị đúng một nửa được làm tròn lên, về phía dương vô cùng, nên2.5thành3và-2.5thành-2. Nó trả vềlongvới đối sốdouble(vàintvớifloat).Math.floorluôn làm tròn xuống, về phía âm vô cùng, và trả vềdouble.Math.ceilluôn làm tròn lên, về phía dương vô cùng, và trả vềdouble.- Ép kiểu
(int)hoàn toàn không phải là làm tròn: nó bỏ phần thập phân, khiến số dương bị giảm xuống còn số âm bị tăng lên.
Vì Math.round(double) trả về long, gán nó cho một int sẽ không biên dịch được nếu không ép kiểu:
int n = Math.round(2.6); // error: incompatible types: possible lossy conversion from long to int
int m = (int) Math.round(2.6); // OK, m is 3
long k = Math.round(2.6); // OK
Để làm tròn tới một số chữ số thập phân, hãy nhân lên, làm tròn rồi chia ngược lại. Chia cho 100.0 (một double) thay vì 100 sẽ giữ được phần thập phân:
Dòng thứ hai chia một long cho một int, đó là phép chia nguyên, nên phần thập phân bị mất. Khi chỉ cần hiển thị số đã làm tròn, String.format("%.2f", value) đơn giản hơn và không thay đổi giá trị đang lưu.
Math.random: số ngẫu nhiên
Math.random() trả về một double lớn hơn hoặc bằng 0.0 và nhỏ hơn 1.0. Mỗi lần gọi cho một giá trị khác nhau, nên đầu ra của các chương trình trong phần này thay đổi mỗi lần bạn nhấn Chạy.
Để lấy một số nguyên trong một khoảng, hãy kéo giãn giá trị đó, ép kiểu rồi dịch chuyển nó. Với một số nguyên từ min đến max, bao gồm cả hai đầu:
int value = (int) (Math.random() * (max - min + 1)) + min;
Đây là cách công thức hoạt động với một con xúc xắc, khi min là 1 và max là 6:
Math.random() * 6cho mộtdoubletừ0.0đến dưới6.0(không bao gồm6.0).(int)bỏ phần thập phân, cho ra một trong các số0, 1, 2, 3, 4, 5.+ 1dịch chúng thành1, 2, 3, 4, 5, 6.
Dòng đầu tiên cho thấy năm lần gieo ngẫu nhiên. Dòng thứ hai kiểm tra mười nghìn lần gieo và trên thực tế luôn in ra lowest: 1, highest: 6: công thức chạm tới cả hai đầu của khoảng và không bao giờ vượt quá.
Cặp ngoặc quanh Math.random() * (max - min + 1) rất quan trọng. Phép ép kiểu áp dụng cho giá trị đứng ngay sau nó, nên (int) Math.random() * 6 ép Math.random() trước. Kết quả đó luôn là 0, và 0 * 6 là 0, nên mọi "lần gieo" đều ra cùng một số.
Math.random() đủ dùng cho trò chơi và bài tập. Với nhu cầu khác, lớp java.util.Random và ThreadLocalRandom.current().nextInt(min, max + 1) (trong java.util.concurrent) trả về số nguyên trực tiếp, và Random có thể nhận một seed để lặp lại cùng một dãy số khi kiểm thử. Không cái nào trong số này phù hợp để tạo mật khẩu hay token bảo mật; hãy dùng java.security.SecureRandom cho những việc đó.
Math.PI và Math.E
Math.PI (π) và Math.E (số Euler e) là các hằng số static final double:
Hãy dùng chúng thay vì gõ 3.14. Chúng chính xác tới mức tối đa mà double cho phép, và cái tên cho người đọc biết con số đó là gì.
Kết quả số nguyên và kết quả double
Phần lớn phương thức của Math nhận và trả về double. Khi bạn truyền giá trị int, Java tự động mở rộng đối số thành double, nhưng phép tính bạn viết trước lời gọi vẫn diễn ra trên int. Đây là nguồn gốc của phần lớn đáp án sai:
items / perBox là 7 / 2, và chia hai giá trị int sẽ bỏ phần dư, nên kết quả là 3 trước khi Math.ceil kịp nhìn thấy. Math.ceil(3.0) là 3.0. Hãy biến một toán hạng thành double, bằng ép kiểu hoặc một giá trị như 4.0, để phép chia giữ được phần thập phân. Trang toán tử giải thích chi tiết hơn về phép chia nguyên.
Quy tắc chung: xác định kiểu bạn muốn có ở cuối, cho phép chia hoặc phép nhân diễn ra trên double khi phần thập phân quan trọng, và chỉ ép về int hoặc long một lần, ở bước cuối cùng.
Các phương thức hữu ích khác
Thêm vài phương thức trong cùng lớp, tất cả đều được gọi theo cùng một cách:
Math.loglà logarit tự nhiên;Math.log10là logarit cơ số 10.Math.floorMod(a, b)cho phần dư mang dấu củab, đúng thứ bạn cần khi xoay vòng trong một khoảng, chẳng hạn các ngày trong tuần. Toán tử%giữ dấu củaa.- Các phương thức lượng giác (
sin,cos,tan) nhận đơn vị radian. Hãy đổi độ sang radian bằngMath.toRadianstrước. Math.addExact,Math.multiplyExactvà các phương thức cùng họ némArithmeticExceptionkhi tràn số, thay vì lặng lẽ quay vòng thành một số âm.
Câu hỏi thường gặp
Cách dùng Math.pow trong Java?
Math.pow(base, exponent) trả về base mũ exponent dưới dạng double, nên Math.pow(2, 3) là 8.0. Để lưu kết quả vào một int, hãy ép kiểu: int n = (int) Math.pow(2, 10); cho ra 1024. Java không có toán tử lũy thừa ^; ^ là phép XOR theo bit.
Làm sao tạo số ngẫu nhiên trong một khoảng bằng Math.random?
Math.random() trả về một double từ 0.0 đến dưới 1.0 (không bao gồm chính số này). Để lấy một số nguyên từ min đến max, bao gồm cả hai đầu, hãy dùng (int) (Math.random() * (max - min + 1)) + min. Gieo một con xúc xắc là (int) (Math.random() * 6) + 1.
Có cần import lớp Math trong Java không?
Không. Math nằm trong package java.lang, được mọi tệp Java tự động import. Mọi phương thức của nó đều là static, nên bạn gọi chúng qua tên lớp, như Math.sqrt(16), mà không cần tạo đối tượng.
Vì sao Math.round trong Java trả về long?
Math.round(double) trả về long vì một double có thể chứa giá trị lớn hơn rất nhiều so với int. Gán kết quả cho một int thì cần ép kiểu: int n = (int) Math.round(2.6);. Còn Math.round(float) trả về int.
Math.floor, Math.ceil và Math.round khác nhau thế nào?
Math.floor làm tròn xuống (2.7 thành 2.0, -2.3 thành -3.0), Math.ceil làm tròn lên (2.1 thành 3.0), còn Math.round làm tròn tới số nguyên gần nhất, giá trị đúng một nửa thì làm tròn lên (2.5 thành 3). floor và ceil trả về double; round trả về long.