Kenapa ini penting
Angka ada di mana-mana dalam program: harga, jumlah stok, koordinat, warna, waktu. Kamu akan sering bertemu tiga sistem bilangan: desimal (yang kita pakai sehari-hari), biner (bahasa asli komputer), dan heksadesimal (cara ringkas menulis biner, misalnya warna CSS #FF5733). Kamu juga perlu tahu bahwa komputer menyimpan angka pecahan dengan cara yang tidak selalu presisi. Kalau tidak tahu, kamu bisa kaget melihat 0.1 + 0.2 menghasilkan 0.30000000000000004, padahal ini bukan bug JavaScript, melainkan sifat cara komputer menyimpan pecahan. Programmer yang membuat aplikasi kasir atau keuangan wajib paham soal ini.
Konsep inti
Sistem bilangan dan nilai tempat
Semua sistem bilangan bekerja dengan nilai tempat. Yang berbeda hanya basis (berapa simbol yang dipakai).
| Sistem | Basis | Simbol | Contoh | Awalan di JavaScript |
|---|---|---|---|---|
| Desimal | 10 | 0-9 | 255 |
(tanpa awalan) |
| Biner | 2 | 0-1 | 11111111 |
0b |
| Oktal | 8 | 0-7 | 377 |
0o |
| Heksadesimal | 16 | 0-9, A-F | FF |
0x |
Di desimal, 352 artinya 3×100 + 5×10 + 2×1. Nilai tempatnya kelipatan 10. Di biner kelipatannya 2 (1, 2, 4, 8, ...), dan di heksadesimal kelipatannya 16 (1, 16, 256, ...).
Di heksadesimal, setelah 9 kita butuh simbol tambahan: A=10, B=11, C=12, D=13, E=14, F=15.
Konversi desimal ke biner (cara bagi 2)
Bagi terus dengan 2, catat sisanya, lalu baca sisanya dari bawah ke atas. Contoh untuk 13:
13 ÷ 2 = 6 sisa 1
6 ÷ 2 = 3 sisa 0
3 ÷ 2 = 1 sisa 1
1 ÷ 2 = 0 sisa 1
Baca dari bawah: 1101
Cek balik: 1×8 + 1×4 + 0×2 + 1×1 = 13. Benar.
Konversi biner ↔ heksadesimal
Ini alasan heksadesimal populer: 1 digit heksa = tepat 4 bit. Jadi 1 byte (8 bit) selalu bisa ditulis dengan 2 digit heksa.
Biner: 1111 1111 → F F → 0xFF = 255
Biner: 0010 1010 → 2 A → 0x2A = 42
Itulah kenapa warna CSS #FF5733 sebenarnya tiga byte: merah FF (255), hijau 57 (87), biru 33 (51).
Integer dan floating point
JavaScript menyimpan hampir semua angka sebagai satu tipe, Number, dengan format IEEE 754 double precision (64 bit). Formatnya mirip notasi ilmiah: ada tanda (+/−), angka penting (mantissa), dan pangkat (exponent). Contohnya 6,02 × 10²³ tapi dalam basis 2.
Konsekuensinya:
- Bilangan bulat aman hanya sampai
Number.MAX_SAFE_INTEGER= 9.007.199.254.740.991. Di atas itu, angka bisa tidak akurat. Untuk angka bulat sangat besar, gunakan tipe BigInt (123n). - Pecahan tertentu tidak bisa disimpan persis. Sama seperti 1/3 di desimal menjadi 0,3333... tanpa akhir, angka 0,1 di biner menjadi
0.0001100110011...yang berulang tanpa akhir. Komputer harus memotongnya di 64 bit, sehingga ada selisih super kecil.
Kasus terkenal: 0.1 + 0.2
Karena 0,1 dan 0,2 sama-sama tersimpan sedikit meleset, hasil penjumlahannya juga meleset: 0.30000000000000004. Ini terjadi di semua bahasa yang memakai IEEE 754 (Python, Java, C juga sama), bukan hanya JavaScript.
Cara menanganinya:
- Untuk tampilan: bulatkan dengan
toFixed(2)(hasilnya string) atauMath.round. - Untuk perbandingan: jangan pakai
===langsung. Cek apakah selisihnya sangat kecil, misalnya lebih kecil dariNumber.EPSILON. - Untuk uang: simpan dalam satuan terkecil sebagai bilangan bulat. Contohnya rupiah utuh, atau sen untuk dolar (Rp 1.500 disimpan
1500, $1,50 disimpan150sen). Hindari menghitung uang dengan pecahan.
Nilai khusus Number
Infinitydan-Infinity, misalnya hasil1 / 0.NaN(Not a Number), hasil operasi angka yang tidak masuk akal, misalnya"abc" * 2. Uniknya,NaNtidak sama dengan dirinya sendiri. Cek denganNumber.isNaN(x).
Contoh kode
// Menulis angka dalam berbagai basis
console.log(0b1101); // → 13 (biner)
console.log(0o17); // → 15 (oktal)
console.log(0xff); // → 255 (heksadesimal)
// Desimal → basis lain: toString(basis)
const n = 255;
console.log(n.toString(2)); // → 11111111
console.log(n.toString(16)); // → ff
console.log(n.toString(16).toUpperCase()); // → FF
// Basis lain → desimal: parseInt(teks, basis)
console.log(parseInt("1101", 2)); // → 13
console.log(parseInt("2A", 16)); // → 42
// Membaca warna CSS heksa menjadi RGB
const warna = "#FF5733";
const r = parseInt(warna.slice(1, 3), 16);
const g = parseInt(warna.slice(3, 5), 16);
const b = parseInt(warna.slice(5, 7), 16);
console.log(`rgb(${r}, ${g}, ${b})`); // → rgb(255, 87, 51)
Floating point dan cara aman menghadapinya:
console.log(0.1 + 0.2); // → 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // → false
// 1) Bulatkan untuk tampilan (hasilnya STRING)
console.log((0.1 + 0.2).toFixed(2)); // → 0.30
// 2) Bandingkan dengan toleransi
const hampirSama = (a, b) => Math.abs(a - b) < Number.EPSILON;
console.log(hampirSama(0.1 + 0.2, 0.3)); // → true
// 3) Uang: hitung dalam satuan terkecil (bilangan bulat)
const hargaSen = 1990; // $19,90 disimpan sebagai 1990 sen
const totalSen = hargaSen * 3; // 5970
console.log((totalSen / 100).toFixed(2)); // → 59.70
// Batas bilangan bulat aman
console.log(Number.MAX_SAFE_INTEGER); // → 9007199254740991
console.log(9007199254740992 + 1); // → 9007199254740992 (tidak akurat!)
console.log(9007199254740992n + 1n); // → 9007199254740993n (BigInt akurat)
// Nilai khusus
console.log(1 / 0); // → Infinity
console.log("abc" * 2); // → NaN
console.log(NaN === NaN); // → false
console.log(Number.isNaN("abc" * 2)); // → true
Kesalahan umum
- Membandingkan pecahan dengan
===.0.1 + 0.2 === 0.3bernilaifalse. Pakai toleransi atau bulatkan dulu. - Lupa bahwa
toFixedmenghasilkan string.(1.5).toFixed(2) + 1hasilnya"1.501", bukan2.5. Ubah balik denganNumber(...)kalau masih mau dihitung. - Menghitung uang dengan pecahan desimal. Simpan dalam satuan terkecil sebagai bilangan bulat untuk menghindari selisih pembulatan yang menumpuk.
- Lupa basis di
parseInt. Selalu tulis basisnya, misalnyaparseInt("08", 10)atauparseInt("ff", 16), supaya maksudmu jelas. - Mengecek NaN dengan
=== NaN. Selalufalse. GunakanNumber.isNaN(x).
Latihan
- Mudah, Ubah angka 10, 64, dan 200 ke biner dan heksadesimal memakai
toString. - Sedang, Buat function
heksKeRgb("#1E90FF")yang mengembalikan teks"rgb(30, 144, 255)". - Menantang, Hitung total belanja 3 barang seharga $0,10, $0,20, dan $0,30 sehingga hasilnya tampil persis
"0.60"dan perbandingannya dengan 0,6 bernilaitrue. Gunakan teknik satuan terkecil (sen).
Lihat solusi
1.
for (const n of [10, 64, 200]) {
console.log(n, n.toString(2), n.toString(16));
}
// → 10 1010 a
// → 64 1000000 40
// → 200 11001000 c8
2.
function heksKeRgb(heks) {
const r = parseInt(heks.slice(1, 3), 16);
const g = parseInt(heks.slice(3, 5), 16);
const b = parseInt(heks.slice(5, 7), 16);
return `rgb(${r}, ${g}, ${b})`;
}
console.log(heksKeRgb("#1E90FF")); // → rgb(30, 144, 255)
3.
const hargaSen = [10, 20, 30]; // semua dalam sen, bilangan bulat
let totalSen = 0;
for (const h of hargaSen) totalSen += h;
console.log(totalSen); // → 60
console.log((totalSen / 100).toFixed(2)); // → 0.60
console.log(totalSen === 60); // → true (bandingkan dalam sen)
// Bandingkan dengan cara naif:
console.log(0.1 + 0.2 + 0.3); // → 0.6000000000000001
Ringkasan
- Desimal (basis 10), biner (basis 2), dan heksadesimal (basis 16) sama-sama memakai nilai tempat.
- 1 digit heksa = 4 bit, jadi 1 byte = 2 digit heksa (contoh warna
#FF5733). toString(basis)mengubah angka ke teks basis lain,parseInt(teks, basis)mengubah balik ke angka.- Number di JavaScript adalah IEEE 754 64-bit. Pecahan seperti 0,1 tidak tersimpan persis, sehingga
0.1 + 0.2 !== 0.3. - Untuk uang, pakai bilangan bulat satuan terkecil. Untuk bilangan bulat sangat besar, pakai BigInt.