Tipe int berhenti di 2.147.483.647 — dan Java tidak akan memberi tahu kita ketika angka tersebut terlampaui. Tambah satu unit ke Integer.MAX_VALUE dan nilainya melipat menjadi -2.147.483.648; tidak ada error, tidak ada exception, tidak ada baris log. Perilaku wrap-around ini pernah menghentikan penghitung penayangan video di YouTube ketika video Gangnam Style menembus 2.147.483.647 tontonan — pelayanan tersebut akhirnya bermigrasi ke penghitung 64 bit. Pada Chapter ini kita bedah mekanisme luapan (overflow), lalu membangun pertahanan bertingkat: deteksi dengan Math.addExact, pencegahan dengan long, dan jangkar terakhir BigInteger.
Mekanisme Melipat #
Aritmetika integer Java bekerja dalam aritmetika modulo 2³² untuk int dan modulo 2⁶⁴ untuk long. Ketika hasil operasi keluar dari rentang, JVM menyimpan 32 (atau 64) bit terbawah hasil tersebut — mekanisme yang persis sama dengan pangkasan bit pada Chapter Type Casting, hanya kali ini terjadi di dalam operasi aritmetika biasa tanpa cast yang kita tulis.
Konsekuensinya simetris dan mengejutkan:
Integer.MAX_VALUE + 1melipat keInteger.MIN_VALUE— dari puncak positif langsung ke dasar negatif.Integer.MIN_VALUE - 1melipat kembali keInteger.MAX_VALUE.Integer.MAX_VALUE * 2menjadi-2, karena bit-bit tinggi yang memuat nilainya terbuang dan hanya tersisa pola biner terbawah.
Luapan yang Tidak Terdengar #
Program berikut memperlihatkan seluruh pola melipat tersebut dalam sekali jalan:
public class LuapanSenyap {
public static void main(String[] args) {
System.out.println("MAX_VALUE : " + Integer.MAX_VALUE);
System.out.println("MAX_VALUE + 1 : " + (Integer.MAX_VALUE + 1));
System.out.println("MIN_VALUE - 1 : " + (Integer.MIN_VALUE - 1));
System.out.println("MAX_VALUE * 2 : " + (Integer.MAX_VALUE * 2));
System.out.println("MAX_VALUE + MAX_VALUE : " + (Integer.MAX_VALUE + Integer.MAX_VALUE));
int views = Integer.MAX_VALUE;
views = views + 1; // melipat diam-diam
System.out.println("views + 1 : " + views);
}
}Output yang Dihasilkan:
MAX_VALUE : 2147483647
MAX_VALUE + 1 : -2147483648
MIN_VALUE - 1 : 2147483647
MAX_VALUE * 2 : -2
MAX_VALUE + MAX_VALUE : -2
views + 1 : -2147483648Perhatikan tiga hal dari keluaran di atas:
- Baris 3 (
MAX_VALUE + 1): nilai terbesarintditambah satu menghasilkan nilai terkecilint. Jarak logisnya hanya satu unit, tetapi hasilnya terpaut hampir 4,3 miliar. - Baris 5-6 (
MAX_VALUE * 2danMAX_VALUE + MAX_VALUE): dua operasi berbeda menghasilkan-2— pola 32 bit terbawah dari hasil yang sebenarnya jauh lebih besar. - Baris 9-11 (
views + 1): skenario dunia nyata — penghitung penayangan yang menyentuh batas lalu melipat menjadi negatif pada baris berikutnya. Aplikasi yang menampilkan angka ini kepada pengguna akan menampilkan nilai yang mustahil tanpa satu pun error di log.
Deteksi dengan Math.addExact #
Class java.lang.Math menyediakan varian operasi yang memeriksa luapan secara eksplisit: addExact, subtractExact, multiplyExact, incrementExact, dan negateExact — masing-masing untuk int dan long. Ketika hasil keluar rentang, method ini melempar ArithmeticException alih-alih melipat diam-diam. Kegagalan menjadi keras, terlihat, dan dapat ditangani.
Program berikut membandingkan deteksi luapan dengan jalur perhitungan yang sehat:
public class DeteksiLuapan {
public static void main(String[] args) {
try {
int hasil = Math.addExact(Integer.MAX_VALUE, 1);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
try {
int hasil = Math.multiplyExact(1_000_000, 3_000);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
int penduduk = 8_600_000; // int aman
long total = (long) penduduk * 365; // naikkan dulu ke long
System.out.println("penduduk * 365 = " + total);
try {
long besar = Long.MAX_VALUE;
long hasil = Math.addExact(besar, 1);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
}
}Output yang Dihasilkan:
terdeteksi: integer overflow
terdeteksi: integer overflow
penduduk * 365 = 3139000000
terdeteksi: long overflowMari kita bedah baris-baris kuncinya:
- Baris 3-8 (
Math.addExact(Integer.MAX_VALUE, 1)): method menghitung, menemukan hasil2147483648tidak muat diint, lalu melemparArithmeticExceptiondengan pesaninteger overflow. Blokcatchmenangkapnya dan mencetakterdeteksi: integer overflow— bandingkan dengan operator+biasa yang membiarkan nilai melipat tanpa suara. - Baris 10-16 (
Math.multiplyExact(1_000_000, 3_000)): hasil matematisnya 3 miliar, melampaui rentangint, sehingga exception yang sama terlempar. Perkalian adalah penyumbang luapan paling aktif karena dua faktor yang sama-sama “masih tampak wajar” dapat melampaui batas begitu digabungkan. - Baris 18-20 (
(long) penduduk * 365): strategi pencegahan — operand dinaikkan kelongsebelum perkalian dihitung, sehingga hasil3139000000disimpan dengan selamat. Tanpa cast di awal,penduduk * 365akan dihitung sebagaiintlebih dulu dan melipat. - Baris 22-28 (
Math.addExact(Long.MAX_VALUE, 1)):longpunya batas juga. Pesan exception-nyalong overflow— varianlongdari method*Exactmenjaga rentang 64 bit dengan cara yang sama.
Ketika long pun Tidak Cukup #
Untuk angka yang melampaui ±9,2 kuintiliun — kriptografi, kombinatorika, ilmu data berskala besar — pustaka standar menyediakan java.math.BigInteger. Kelas ini menyimpan angka dengan presisi tak terbatas di heap, sehingga tidak pernah melipat:
import java.math.BigInteger;
public class AngkaTakTerbatas {
public static void main(String[] args) {
BigInteger sangatBesar = new BigInteger("9223372036854775808"); // Long.MAX_VALUE + 1
BigInteger dua = sangatBesar.multiply(BigInteger.TWO);
System.out.println("nilai : " + sangatBesar);
System.out.println("kali 2 : " + dua);
}
}Output yang Dihasilkan:
nilai : 9223372036854775808
kali 2 : 18446744073709551616Program di atas memulai dari angka yang sudah satu unit di luar jangkauan long (2⁶³), lalu mengalikannya dengan dua — operasi mustahil di tipe primitive — dan hasil 18446744073709551616 (2⁶⁴) tercetak utuh. Harganya jelas: BigInteger adalah objek dengan alokasi memori dan operasi method (multiply, bukan *), jadi puluhan kali lebih lambat daripada long. Gunakan hanya ketika rentang memang menuntut.
Luapan integer adalah contoh klasik bahwa “kode berjalan tanpa error” belum berarti “kode benar”. Di ranah floating-point, kelas persoalan yang serupa muncul dalam wujud berbeda: bukan nilai yang melipat, melainkan angka desimal yang tidak pernah bisa disimpan secara persis. Chapter Floating-Point Precision membedah mengapa 0.1 + 0.2 != 0.3 dan bagaimana menyiasatinya.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AngkaTakTerbatas.java
Unduh berkasimport java.math.BigInteger;
public class AngkaTakTerbatas {
public static void main(String[] args) {
BigInteger sangatBesar = new BigInteger("9223372036854775808"); // Long.MAX_VALUE + 1
BigInteger dua = sangatBesar.multiply(BigInteger.TWO);
System.out.println("nilai : " + sangatBesar);
System.out.println("kali 2 : " + dua);
}
}
DeteksiLuapan.java
Unduh berkaspublic class DeteksiLuapan {
public static void main(String[] args) {
try {
int hasil = Math.addExact(Integer.MAX_VALUE, 1);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
try {
int hasil = Math.multiplyExact(1_000_000, 3_000);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
int penduduk = 8_600_000; // int aman
long total = (long) penduduk * 365; // naikkan dulu ke long
System.out.println("penduduk * 365 = " + total);
try {
long besar = Long.MAX_VALUE;
long hasil = Math.addExact(besar, 1);
System.out.println("tidak terjadi luapan: " + hasil);
} catch (ArithmeticException e) {
System.out.println("terdeteksi: " + e.getMessage());
}
}
}
LuapanSenyap.java
Unduh berkaspublic class LuapanSenyap {
public static void main(String[] args) {
System.out.println("MAX_VALUE : " + Integer.MAX_VALUE);
System.out.println("MAX_VALUE + 1 : " + (Integer.MAX_VALUE + 1));
System.out.println("MIN_VALUE - 1 : " + (Integer.MIN_VALUE - 1));
System.out.println("MAX_VALUE * 2 : " + (Integer.MAX_VALUE * 2));
System.out.println("MAX_VALUE + MAX_VALUE : " + (Integer.MAX_VALUE + Integer.MAX_VALUE));
int views = Integer.MAX_VALUE;
views = views + 1; // melipat diam-diam
System.out.println("views + 1 : " + views);
}
}