Math #
Operasi matematika di Java terlihat sederhana di permukaan — ada operator +, -, *, / dan class Math dengan berbagai fungsi. Tapi di baliknya ada jebakan yang tidak kelihatan: integer overflow yang diam-diam menghasilkan angka salah, pembagian floating point yang tidak akurat untuk kalkulasi keuangan, dan double yang tidak bisa merepresentasikan banyak nilai desimal secara tepat. Memahami keterbatasan tipe numerik Java dan kapan harus menggantinya dengan BigDecimal atau BigInteger adalah keterampilan yang membedakan developer yang menulis kode yang “tampak benar” dari developer yang menulis kode yang benar-benar benar. Artikel ini membahas seluruh toolkit matematika Java — dari Math.abs() yang paling sederhana hingga BigDecimal untuk transaksi keuangan dan SecureRandom untuk kriptografi.
Class Math — Fungsi Matematika Standar #
java.lang.Math berisi method statik untuk semua operasi matematika umum. Semua method menggunakan tipe double kecuali yang disebutkan secara eksplisit.
Nilai Absolut, Min, dan Max #
// abs() — nilai absolut
Math.abs(-5); // 5
Math.abs(-3.14); // 3.14
Math.abs(Integer.MIN_VALUE); // JEBAKAN! hasilnya Integer.MIN_VALUE karena overflow!
// Integer.MIN_VALUE = -2147483648, dan Math.abs(-2147483648) overflow menjadi -2147483648
// ✓ BENAR: gunakan Math.absExact() (Java 15+) untuk deteksi overflow
try {
Math.absExact(Integer.MIN_VALUE); // throw ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Overflow terdeteksi: " + e.getMessage());
}
// min() dan max()
Math.min(3, 7); // 3
Math.max(3, 7); // 7
Math.min(3.5, 2.1); // 2.1
Math.min(Double.NaN, 3.0); // NaN — NaN "menginfeksi" perbandingan
// clamp() — Java 21+
// memastikan nilai berada dalam rentang [min, max]
double nilai = 150.0;
double terbatas = Math.clamp(nilai, 0.0, 100.0); // 100.0
int terbatasInt = Math.clamp(250, 0, 100); // 100
Pembulatan #
// Empat jenis pembulatan yang berbeda — pilih yang tepat sesuai konteks
double d = 2.7;
Math.ceil(d); // 3.0 — selalu ke atas (ceiling)
Math.floor(d); // 2.0 — selalu ke bawah (floor)
Math.round(d); // 3L — round half up (0.5 ke atas), kembalikan long
Math.rint(d); // 3.0 — round half even (banker's rounding), kembalikan double
// Contoh pembulatan 0.5
Math.round(2.5); // 3 — round half up
Math.rint(2.5); // 2.0 — round half even (ke bilangan genap terdekat)
Math.rint(3.5); // 4.0 — round half even
// JEBAKAN: round(double) vs round(float) punya perilaku berbeda untuk nilai negatif
Math.round(-2.5); // -2 (bukan -3!) — round half up artinya ke arah +infinity
Math.round(-3.5); // -3
// truncate — buang bagian desimal (ke arah nol)
(int) 2.9; // 2 — cast truncate
(int) -2.9; // -2 — selalu ke arah nol, bukan ke bawah
Math.floor(-2.9); // -3.0 — ke bawah (lebih negatif)
Pangkat, Akar, dan Logaritma #
// Pangkat
Math.pow(2, 10); // 1024.0 — 2^10
Math.pow(9, 0.5); // 3.0 — akar kuadrat dari 9
Math.pow(27, 1.0/3); // 3.0 — akar kubik dari 27 (HATI-HATI: presisi floating point)
// Akar kuadrat dan kubik
Math.sqrt(16); // 4.0
Math.cbrt(27); // 3.0 — lebih akurat dari Math.pow(x, 1.0/3)
// Logaritma
Math.log(Math.E); // 1.0 — logaritma natural (ln)
Math.log(1); // 0.0
Math.log(0); // -Infinity
Math.log(-1); // NaN
Math.log10(1000); // 3.0 — logaritma basis 10
Math.log10(1); // 0.0
// log basis sembarang — tidak ada method langsung, gunakan change of base
double logBasis2 = (n) -> Math.log(n) / Math.log(2);
// logBasis2(8) = 3.0
// exp() — e^x
Math.exp(1); // 2.718... (nilai e)
Math.exp(0); // 1.0
// hypot() — √(x² + y²) — lebih aman dari Math.sqrt(x*x + y*y) (hindari overflow)
Math.hypot(3, 4); // 5.0
Trigonometri #
// Semua fungsi trigonometri menerima dan mengembalikan radian
// Konstanta
double PI = Math.PI; // 3.141592653589793
double E = Math.E; // 2.718281828459045
// Konversi derajat ↔ radian
double radian = Math.toRadians(90); // π/2 ≈ 1.5707963...
double derajat = Math.toDegrees(Math.PI); // 180.0
// Fungsi trigonometri dasar
Math.sin(Math.toRadians(30)); // 0.5
Math.cos(Math.toRadians(60)); // 0.5
Math.tan(Math.toRadians(45)); // 1.0 (hampir, karena floating point)
// Invers
Math.asin(0.5); // 0.5236... radian (30°)
Math.acos(0.5); // 1.0472... radian (60°)
Math.atan(1.0); // 0.7854... radian (45°)
// atan2 — sudut dari titik asal ke (x, y), mempertimbangkan kuadran
Math.atan2(1, 1); // 0.7854... radian ( 45°)
Math.atan2(1, -1); // 2.3562... radian (135°)
Math.atan2(-1, -1); // -2.3562... radian (-135°, atau 225°)
// Hiperbolik
Math.sinh(1); // 1.1752...
Math.cosh(1); // 1.5431...
Math.tanh(1); // 0.7616...
Aritmetika Integer yang Aman #
Integer overflow adalah bug yang sering tidak disadari karena Java tidak melempar exception — hasilnya hanya “wrap around” secara diam-diam.
// ✗ JEBAKAN: overflow integer yang diam-diam
int a = Integer.MAX_VALUE; // 2147483647
int b = a + 1; // -2147483648 — OVERFLOW! bukan exception!
System.out.println(b); // -2147483648
int hasil = 100000 * 100000; // OVERFLOW: 10_000_000_000 tidak muat di int
System.out.println(hasil); // 1410065408 — salah!
// ✓ BENAR: gunakan method Math.*Exact() untuk deteksi overflow (Java 8+)
try {
int aman = Math.addExact(Integer.MAX_VALUE, 1); // throw ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Overflow terdeteksi: " + e.getMessage());
}
// Semua operasi *Exact tersedia:
Math.addExact(a, b); // a + b, throw jika overflow
Math.subtractExact(a, b); // a - b, throw jika overflow
Math.multiplyExact(a, b); // a * b, throw jika overflow
Math.incrementExact(a); // a + 1, throw jika overflow
Math.decrementExact(a); // a - 1, throw jika overflow
Math.negateExact(a); // -a, throw jika overflow (khusus MIN_VALUE)
Math.toIntExact(longVal); // cast long ke int, throw jika tidak muat
// ✓ ALTERNATIF: gunakan long jika nilai bisa besar
long produk = (long) 100000 * 100000; // 10000000000L — aman
System.out.println(produk); // 10000000000
// Deteksi overflow manual untuk kode kritis
public static boolean akanOverflow(int a, int b) {
return ((long) a + b) != (int) ((long) a + b);
}
Aritmetika Floating Point — Jebakan yang Harus Diwaspadai #
// ✗ JEBAKAN KLASIK: floating point tidak merepresentasikan semua desimal secara tepat
double a = 0.1 + 0.2;
System.out.println(a); // 0.30000000000000004 — BUKAN 0.3!
System.out.println(a == 0.3); // false!
// ✗ ANTI-PATTERN: membandingkan double dengan ==
if (0.1 + 0.2 == 0.3) { // JANGAN lakukan ini!
System.out.println("sama");
}
// ✓ BENAR: bandingkan dengan epsilon (toleransi)
double epsilon = 1e-10;
if (Math.abs((0.1 + 0.2) - 0.3) < epsilon) {
System.out.println("cukup sama");
}
// ✓ LEBIH BAIK: untuk uang dan kalkulasi presisi — gunakan BigDecimal
// Nilai khusus floating point
double infPos = Double.POSITIVE_INFINITY; // 1.0 / 0.0
double infNeg = Double.NEGATIVE_INFINITY; // -1.0 / 0.0
double nan = Double.NaN; // 0.0 / 0.0 atau Math.sqrt(-1)
// Cek nilai khusus
Double.isInfinite(infPos); // true
Double.isNaN(nan); // true
Double.isFinite(3.14); // true (Java 8+)
// NaN tidak sama dengan dirinya sendiri!
System.out.println(nan == nan); // false — satu-satunya nilai yang tidak == dirinya
System.out.println(Double.isNaN(nan)); // true — cara yang benar
// Underflow dan subnormal
double kecil = Double.MIN_VALUE; // 4.9E-324 — nilai positif terkecil yang terwakili
double terlalukecil = kecil / 2; // 0.0 — underflow ke nol
BigDecimal — Kalkulasi Presisi untuk Keuangan #
Untuk kalkulasi yang melibatkan uang, harga, bunga, atau nilai apapun yang membutuhkan presisi desimal, selalu gunakan BigDecimal, bukan double atau float.
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.math.MathContext;
public class BigDecimalDemo {
public void demo() {
// ✗ ANTI-PATTERN: uang sebagai double
double hargaDouble = 1.10;
double pajakDouble = hargaDouble * 0.1;
System.out.println("Pajak (double): " + pajakDouble); // 0.11000000000000001!
// ✓ BENAR: uang sebagai BigDecimal
// PENTING: selalu gunakan String constructor, BUKAN double constructor!
BigDecimal harga = new BigDecimal("1.10"); // ✓ tepat: "1.10"
BigDecimal hargaSalah = new BigDecimal(1.10); // ✗ tidak tepat: 1.0999999...
BigDecimal hargaOf = BigDecimal.valueOf(1.10); // ✓ tepat (konversi via String)
// Operasi aritmetika
BigDecimal diskon = new BigDecimal("0.10");
BigDecimal total = harga.add(new BigDecimal("2.50"));
BigDecimal setelahDiskon = total.subtract(total.multiply(diskon));
BigDecimal perUnit = total.divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);
System.out.println("Total: " + total); // 3.60
System.out.println("Setelah diskon: " + setelahDiskon); // 3.240
System.out.println("Per unit: " + perUnit); // 1.20
// Pembagian yang menghasilkan desimal tak berakhir BUTUH skala eksplisit!
// ✗ ANTI-PATTERN: pembagian tanpa skala — throw ArithmeticException
// new BigDecimal("1").divide(new BigDecimal("3")); // Exception!
// ✓ BENAR: tentukan skala (jumlah desimal) dan mode pembulatan
BigDecimal sepertiga = new BigDecimal("1").divide(
new BigDecimal("3"),
10, // 10 digit desimal
RoundingMode.HALF_UP
);
System.out.println(sepertiga); // 0.3333333333
// RoundingMode — pilih berdasarkan konteks bisnis
BigDecimal nilai = new BigDecimal("2.555");
System.out.println(nilai.setScale(2, RoundingMode.HALF_UP)); // 2.56 — umum
System.out.println(nilai.setScale(2, RoundingMode.HALF_DOWN)); // 2.55
System.out.println(nilai.setScale(2, RoundingMode.HALF_EVEN)); // 2.56 (banker's rounding)
System.out.println(nilai.setScale(2, RoundingMode.CEILING)); // 2.56 — selalu naik
System.out.println(nilai.setScale(2, RoundingMode.FLOOR)); // 2.55 — selalu turun
System.out.println(nilai.setScale(2, RoundingMode.UP)); // 2.56 — menjauhi nol
System.out.println(nilai.setScale(2, RoundingMode.DOWN)); // 2.55 — ke arah nol
// Komparasi BigDecimal — JANGAN gunakan equals() untuk membandingkan nilai!
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
System.out.println(a.equals(b)); // false! skala berbeda (1 vs 2)
System.out.println(a.compareTo(b) == 0); // true — bandingkan nilai matematisnya
// ✓ Pola yang benar untuk perbandingan
boolean sama = a.compareTo(b) == 0; // nilai sama
boolean lebihBesar = a.compareTo(b) > 0; // a > b
boolean lebihKecil = a.compareTo(b) < 0; // a < b
// Operasi lain
BigDecimal absolut = new BigDecimal("-5.5").abs(); // 5.5
BigDecimal pangkat = new BigDecimal("2").pow(10); // 1024
BigDecimal maks = a.max(b);
BigDecimal mins = a.min(b);
// Mengambil nilai
int intVal = nilai.intValue(); // truncate ke int
long longVal = nilai.longValue(); // truncate ke long
double doubleVal = nilai.doubleValue(); // konversi ke double (presisi bisa hilang)
String strVal = nilai.toPlainString(); // "2.555" (bukan notasi ilmiah)
String strValSci = nilai.toString(); // bisa "2.555" atau "2.555E+0"
// Konstanta yang berguna
BigDecimal nol = BigDecimal.ZERO;
BigDecimal satu = BigDecimal.ONE;
BigDecimal sepuluh = BigDecimal.TEN;
}
}
Contoh Kalkulasi Keuangan #
public class KalkulasiKeuangan {
private static final BigDecimal SERATUS = new BigDecimal("100");
// Hitung total harga dengan pajak
public BigDecimal hitungTotal(BigDecimal harga, BigDecimal pajakPersen) {
BigDecimal pajak = harga.multiply(pajakPersen)
.divide(SERATUS, 2, RoundingMode.HALF_UP);
return harga.add(pajak).setScale(2, RoundingMode.HALF_UP);
}
// Hitung angsuran (simplified)
public BigDecimal hitungAngsuran(BigDecimal pokok, BigDecimal bungaPersen, int bulan) {
// r = bunga per bulan
BigDecimal r = bungaPersen.divide(
SERATUS.multiply(new BigDecimal("12")),
10, RoundingMode.HALF_UP
);
// Angsuran = P * r * (1+r)^n / ((1+r)^n - 1)
BigDecimal satu = BigDecimal.ONE;
BigDecimal satu_plus_r = satu.add(r);
BigDecimal pangkat = satu_plus_r.pow(bulan);
return pokok.multiply(r).multiply(pangkat)
.divide(pangkat.subtract(satu), 2, RoundingMode.HALF_UP);
}
// Bandingkan harga dengan benar
public boolean lebihMahal(BigDecimal harga1, BigDecimal harga2) {
return harga1.compareTo(harga2) > 0;
}
}
BigInteger — Bilangan Bulat Arbitrarily Large #
BigInteger digunakan ketika nilai integer melebihi kapasitas long (lebih dari ±9.2 × 10¹⁸) atau saat kamu butuh operasi kriptografi.
import java.math.BigInteger;
public class BigIntegerDemo {
public void demo() {
// Buat BigInteger
BigInteger a = new BigInteger("123456789012345678901234567890");
BigInteger b = BigInteger.valueOf(42);
BigInteger duaPangkat100 = BigInteger.TWO.pow(100); // 2^100
// Operasi aritmetika — semua mengembalikan BigInteger baru (immutable)
BigInteger jumlah = a.add(b);
BigInteger selisih = a.subtract(b);
BigInteger kali = a.multiply(b);
BigInteger[] bagiSisa = a.divideAndRemainder(b); // [hasil_bagi, sisa]
BigInteger bagi = a.divide(b);
BigInteger sisa = a.mod(b);
BigInteger pangkat = b.pow(20); // 42^20
// Operasi bitwise
BigInteger dan = a.and(b);
BigInteger atau = a.or(b);
BigInteger xor = a.xor(b);
BigInteger geserKiri = a.shiftLeft(10); // a * 2^10
BigInteger geserKanan = a.shiftRight(10); // a / 2^10
// Fungsi matematika
BigInteger absolut = new BigInteger("-42").abs(); // 42
BigInteger maks = a.max(b);
BigInteger mins = a.min(b);
BigInteger fgb = a.gcd(b); // greatest common divisor
// Bilangan prima
boolean primaBesar = a.isProbablePrime(100); // Miller-Rabin dengan 100 iterasi
BigInteger primaTerdekat = a.nextProbablePrime();
// Komparasi
int cmp = a.compareTo(b); // < 0, == 0, atau > 0
boolean sama = a.equals(b); // safe untuk BigInteger (berbeda dari BigDecimal!)
// Konversi
long longVal = b.longValue(); // throw ArithmeticException jika tidak muat: longValueExact()
int intVal = b.intValueExact(); // Java 8+ — throw jika tidak muat
byte[] bytes = a.toByteArray(); // representasi byte (two's complement)
String hex = a.toString(16); // representasi hex
String biner = a.toString(2); // representasi biner
// Konstanta
BigInteger nol = BigInteger.ZERO;
BigInteger satu = BigInteger.ONE;
BigInteger dua = BigInteger.TWO;
BigInteger sepuluh = BigInteger.TEN;
}
// Faktorial — contoh nyata kebutuhan BigInteger
public BigInteger faktorial(int n) {
BigInteger hasil = BigInteger.ONE;
for (int i = 2; i <= n; i++) {
hasil = hasil.multiply(BigInteger.valueOf(i));
}
return hasil;
}
// faktorial(100) = 93326215443944152681699238856266700490715968264381...
// (158 digit — tidak muat di long!)
// Fibonacci dengan BigInteger
public BigInteger fibonacci(int n) {
BigInteger a = BigInteger.ZERO;
BigInteger b = BigInteger.ONE;
for (int i = 0; i < n; i++) {
BigInteger temp = a.add(b);
a = b;
b = temp;
}
return a;
}
}
Random — Angka Acak #
Java menyediakan beberapa kelas untuk membangkitkan angka acak dengan karakteristik berbeda.
java.util.Random #
import java.util.Random;
public class RandomDemo {
public void demo() {
Random rng = new Random();
// Integer acak
int acak = rng.nextInt(); // semua range int
int acakBatas = rng.nextInt(100); // 0 sampai 99 (eksklusif)
int acakRange = rng.nextInt(50, 100); // 50 sampai 99 (Java 17+)
// Tipe lain
long acakLong = rng.nextLong();
long acakLongRange = rng.nextLong(1000L); // 0 sampai 999 (Java 17+)
double acakDouble = rng.nextDouble(); // 0.0 sampai < 1.0
double acakDoubleRange = rng.nextDouble(1.0, 10.0); // Java 17+
boolean acakBool = rng.nextBoolean();
// Distribusi Gaussian (normal distribution)
double gaussian = rng.nextGaussian(); // mean=0, std=1
// Stream angka acak (Java 8+)
int[] arrayAcak = rng.ints(10, 1, 101) // 10 angka, 1-100
.toArray();
double[] doubleAcak = rng.doubles(5, 0.0, 1.0) // 5 double, 0-1
.toArray();
// Seed deterministik — untuk testing dan reproduktibilitas
Random seeded = new Random(12345L); // seed tetap
int nilai1 = seeded.nextInt(100); // SELALU menghasilkan nilai yang sama
int nilai2 = seeded.nextInt(100); // urutan yang deterministik
// Acak elemen dari array
int[] data = {10, 20, 30, 40, 50};
int elemenAcak = data[rng.nextInt(data.length)];
// Shuffle array
Integer[] arr = {1, 2, 3, 4, 5};
java.util.List<Integer> list = java.util.Arrays.asList(arr);
java.util.Collections.shuffle(list, rng);
}
}
ThreadLocalRandom — Lebih Cepat untuk Multi-thread #
import java.util.concurrent.ThreadLocalRandom;
public class ThreadLocalRandomDemo {
// ✗ ANTI-PATTERN: berbagi satu instance Random di banyak thread — kontensi!
private static final Random SHARED_RANDOM = new Random();
// ✓ BENAR: ThreadLocalRandom — satu instance per thread, tidak ada kontensi
public void demo() {
// Tidak perlu menyimpan instance — akses via static method
int acak = ThreadLocalRandom.current().nextInt(1, 101); // 1 sampai 100
// Di dalam stream paralel — ThreadLocalRandom digunakan secara otomatis
int[] hasil = ThreadLocalRandom.current().ints(1000, 0, 100).toArray();
}
}
SecureRandom — Angka Acak Kriptografis #
Untuk kebutuhan keamanan (token, password, key kriptografi), Random biasa tidak cukup karena output-nya dapat diprediksi jika seed-nya diketahui.
import java.security.SecureRandom;
public class SecureRandomDemo {
// ✗ ANTI-PATTERN: Random untuk token keamanan — dapat diprediksi!
public String buatTokenTidakAman() {
Random rng = new Random();
StringBuilder token = new StringBuilder();
for (int i = 0; i < 32; i++) {
token.append(Integer.toHexString(rng.nextInt(16)));
}
return token.toString(); // TIDAK AMAN
}
// ✓ BENAR: SecureRandom untuk token keamanan
private static final SecureRandom SECURE_RNG = new SecureRandom();
private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
public String buatTokenAman(int panjangByte) {
byte[] bytes = new byte[panjangByte];
SECURE_RNG.nextBytes(bytes);
// Konversi ke hex string
StringBuilder sb = new StringBuilder(panjangByte * 2);
for (byte b : bytes) {
sb.append(HEX_CHARS[(b >> 4) & 0xF]);
sb.append(HEX_CHARS[b & 0xF]);
}
return sb.toString();
}
// Atau menggunakan Base64 untuk token yang lebih pendek
public String buatTokenBase64(int panjangByte) {
byte[] bytes = new byte[panjangByte];
SECURE_RNG.nextBytes(bytes);
return java.util.Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(bytes);
}
// Angka acak dalam range (aman)
public int angkaAmanDalamRange(int min, int max) {
// nextInt(bound) dari SecureRandom menghindari modulo bias
return min + SECURE_RNG.nextInt(max - min);
}
}
SecureRandomjauh lebih lambat dariRandombiasa karena menggunakan sumber entropi dari OS (seperti/dev/urandomdi Linux). Gunakan hanya untuk kebutuhan kriptografi — token sesi, password reset, API key. Untuk simulasi, game, atau test data,RandomatauThreadLocalRandomsudah cukup.
Konversi Numerik #
Antara Tipe Primitif #
// Widening — otomatis, tidak kehilangan data
byte b = 100;
short s = b; // otomatis
int i = s; // otomatis
long l = i; // otomatis
float f = l; // otomatis — tapi HATI-HATI: float hanya 23 bit mantissa
double d = f; // otomatis
// Narrowing — harus eksplisit dengan cast, bisa kehilangan data
double nilaiDouble = 9.99;
int nilaiInt = (int) nilaiDouble; // 9 — bagian desimal dibuang (truncate, bukan round!)
long nilaiLong = 300L;
byte nilaiByte = (byte) nilaiLong; // 44 — overflow, wrap around!
// Cara aman untuk narrowing
try {
int exact = Math.toIntExact(nilaiLong); // throw jika tidak muat
} catch (ArithmeticException e) {
System.out.println("Nilai tidak muat dalam int");
}
// String ke numerik (sudah dibahas di Strings, diulang untuk kelengkapan)
int dariString = Integer.parseInt("42");
double dariStringD = Double.parseDouble("3.14");
BigDecimal dariStringBD = new BigDecimal("1234.56");
Representasi Bit dan Byte #
// Representasi bit dari float/double
int bitFloat = Float.floatToIntBits(3.14f); // bit pattern sebagai int
int bitFloatRaw = Float.floatToRawIntBits(3.14f); // tanpa normalisasi NaN
float kembali = Float.intBitsToFloat(bitFloat); // 3.14
long bitDouble = Double.doubleToLongBits(3.14);
double kembaliD = Double.longBitsToDouble(bitDouble);
// Representasi integer sebagai string berbagai basis
String hex = Integer.toHexString(255); // "ff"
String biner = Integer.toBinaryString(10); // "1010"
String oktal = Integer.toOctalString(8); // "10"
// Parsing dari berbagai basis
int dariHex = Integer.parseInt("FF", 16); // 255
int dariBiner = Integer.parseInt("1010", 2); // 10
// Operasi bit tingkat rendah
Integer.bitCount(255); // 8 — jumlah bit 1
Integer.highestOneBit(100); // 64 — bit tertinggi yang aktif
Integer.lowestOneBit(100); // 4 — bit terendah yang aktif
Integer.numberOfLeadingZeros(1); // 31
Integer.numberOfTrailingZeros(8); // 3
Integer.reverse(1); // 2147483648 — balik semua bit
Integer.reverseBytes(0x12345678); // 0x78563412
Kapan Menggunakan Tipe Numerik yang Mana #
double / float
✓ Kalkulasi saintifik dan engineering
✓ Koordinat grafis dan geometri
✓ Statistik dan machine learning
✗ JANGAN untuk uang, harga, atau nilai keuangan
✗ JANGAN untuk nilai yang dibandingan dengan ==
int / long
✓ Counter, index, ID, timestamp
✓ Bit manipulation
✗ JANGAN jika nilai bisa overflow — gunakan *Exact() atau BigInteger
BigDecimal
✓ Uang, harga, nilai keuangan
✓ Kalkulasi yang butuh presisi desimal tepat
✓ Tarif pajak, bunga, diskon
✗ JANGAN buat dari double — selalu dari String atau valueOf()
✗ JANGAN gunakan equals() untuk perbandingan nilai — gunakan compareTo()
BigInteger
✓ Bilangan yang melebihi Long.MAX_VALUE
✓ Kriptografi (RSA, DH key generation)
✓ Faktorial, Fibonacci besar, kombinatorik
✗ Lebih lambat dan makan lebih banyak memori dari int/long
Random vs ThreadLocalRandom vs SecureRandom
Random → single-threaded, seed deterministik untuk testing
ThreadLocalRandom → multi-threaded, lebih cepat dari Random
SecureRandom → kriptografi — token, password, key
Ringkasan #
- Integer overflow diam-diam — Java tidak melempar exception saat
intataulongoverflow. GunakanMath.addExact(),Math.multiplyExact(), dan sejenisnya untuk deteksi overflow di kalkulasi kritis.doubletidak akurat untuk uang —0.1 + 0.2 != 0.3di floating point. GunakanBigDecimaluntuk semua kalkulasi keuangan tanpa kecuali.- Buat
BigDecimaldariString, bukan daridouble—new BigDecimal("0.1")tepat,new BigDecimal(0.1)mewarisi ketidakakuratan floating point.- Bandingkan
BigDecimaldengancompareTo(), bukanequals()—new BigDecimal("2.0").equals(new BigDecimal("2.00"))mengembalikanfalsekarena skala berbeda.Math.round()dengan nilai negatif 0.5 berperilaku tidak intuitif —-2.5dibulatkan ke-2, bukan-3, karena “round half up” artinya ke arah positif infinity. GunakanRoundingMode.HALF_UPdenganBigDecimaluntuk hasil yang lebih prediktabel.ThreadLocalRandomuntuk multi-thread — lebih efisien dari berbagi satu instanceRandomyang menyebabkan kontensi di lingkungan concurrent.SecureRandomhanya untuk kriptografi — jauh lebih lambat dariRandom, gunakan hanya untuk token, password, dan key yang membutuhkan ketidakpastian kriptografis.NaN != NaNadalah satu-satunya nilai di Java yang tidak sama dengan dirinya sendiri. Selalu gunakanDouble.isNaN()untuk memeriksa NaN, bukan==.
← Sebelumnya: IO