Math

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);
    }
}
SecureRandom jauh lebih lambat dari Random biasa karena menggunakan sumber entropi dari OS (seperti /dev/urandom di Linux). Gunakan hanya untuk kebutuhan kriptografi — token sesi, password reset, API key. Untuk simulasi, game, atau test data, Random atau ThreadLocalRandom sudah 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 int atau long overflow. Gunakan Math.addExact(), Math.multiplyExact(), dan sejenisnya untuk deteksi overflow di kalkulasi kritis.
  • double tidak akurat untuk uang0.1 + 0.2 != 0.3 di floating point. Gunakan BigDecimal untuk semua kalkulasi keuangan tanpa kecuali.
  • Buat BigDecimal dari String, bukan dari doublenew BigDecimal("0.1") tepat, new BigDecimal(0.1) mewarisi ketidakakuratan floating point.
  • Bandingkan BigDecimal dengan compareTo(), bukan equals()new BigDecimal("2.0").equals(new BigDecimal("2.00")) mengembalikan false karena skala berbeda.
  • Math.round() dengan nilai negatif 0.5 berperilaku tidak intuitif — -2.5 dibulatkan ke -2, bukan -3, karena “round half up” artinya ke arah positif infinity. Gunakan RoundingMode.HALF_UP dengan BigDecimal untuk hasil yang lebih prediktabel.
  • ThreadLocalRandom untuk multi-thread — lebih efisien dari berbagi satu instance Random yang menyebabkan kontensi di lingkungan concurrent.
  • SecureRandom hanya untuk kriptografi — jauh lebih lambat dari Random, gunakan hanya untuk token, password, dan key yang membutuhkan ketidakpastian kriptografis.
  • NaN != NaN adalah satu-satunya nilai di Java yang tidak sama dengan dirinya sendiri. Selalu gunakan Double.isNaN() untuk memeriksa NaN, bukan ==.

← Sebelumnya: IO
About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact