Tràn số nguyên (Integer Overflow)
Vấn đề
Phần tiêu đề “Vấn đề”Mỗi kiểu số nguyên trong Java có một phạm vi giá trị cố định. Khi kết quả một phép tính vượt quá phạm vi đó, giá trị sẽ “quay vòng” (wrap around) một cách âm thầm, không ném ra lỗi hay cảnh báo gì:
int max = Integer.MAX_VALUE; // 2147483647System.out.println(max + 1); // -2147483648 - quay vòng về số ÂM nhỏ nhất, KHÔNG có lỗi nào!byte b = 127; // byte tối đa là 127b++;System.out.println(b); // -128 - tràn số âm thầmVì sao nguy hiểm?
Phần tiêu đề “Vì sao nguy hiểm?”- Không có exception, không có cảnh báo lúc biên dịch hay lúc chạy - chương trình vẫn chạy tiếp với giá trị sai
- Rất dễ gặp khi nhân hai số
intlớn (kết quả trung gian tràn dù kết quả cuối cùng để tronglongthì đủ chứa):
int a = 100_000;int b = 100_000;long result = a * b; // NGUY HIỂM! a * b được tính bằng int TRƯỚC, tràn số, rồi mới gán sang longSystem.out.println(result); // 1410065408 - SAI! (kết quả đúng phải là 10,000,000,000)- Thường gặp trong các bài toán tính tổng/tích lớn, tính hash, hoặc bộ đếm chạy lâu dài
Giải pháp
Phần tiêu đề “Giải pháp”1. Ép kiểu sang long trước khi tính, nếu kết quả có thể vượt phạm vi int:
int a = 100_000;int b = 100_000;long result = (long) a * b; // ép a sang long TRƯỚC khi nhân -> phép nhân dùng long, không trànSystem.out.println(result); // 10000000000 - ĐÚNG2. Dùng các phương thức “an toàn” (Math.addExact, Math.multiplyExact,…) để ném exception khi tràn thay vì âm thầm sai:
try { int result = Math.addExact(Integer.MAX_VALUE, 1);} catch (ArithmeticException e) { System.out.println("Phát hiện tràn số: " + e.getMessage());}3. Dùng BigInteger nếu cần số nguyên lớn tùy ý, không giới hạn:
import java.math.BigInteger;
BigInteger big = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);System.out.println(big); // vẫn tính đúng, không trànTóm tắt
Phần tiêu đề “Tóm tắt”Tràn số nguyên: khi kết quả vượt phạm vi kiểu số, Java âm thầm “quay vòng” sang giá trị sai mà không báo lỗi.
- Ép kiểu sang
long/doubletrước khi tính nếu kết quả trung gian có thể vượt phạm viint- Dùng
Math.addExact()/multiplyExact()để phát hiện tràn số bằng exception thay vì âm thầm sai- Dùng
BigIntegerkhi cần số nguyên lớn tùy ý