Setiap perhitungan dalam program akhirnya bermuara ke angka: total belanja Siti, rata-rata penjualan harian, diskon 15%, hingga pembagian untung. JavaScript menyimpan semua angka — bulat maupun desimal — dalam satu tipe yang sama bernama number. Satu tipe untuk semua kebutuhan terasa sederhana, tetapi kesederhanaan itu menyimpan beberapa perilaku yang wajib dipahami sebelum perhitungan pertama kita meleset tanpa terlihat.

Chapter ini membuktikan perilaku tipe number lewat empat eksperimen di Node: batas aman integer, ketidaktepatan floating point, pembulatan untuk tampilan, serta dua nilai istimewa — Infinity dan NaN. Setiap keluaran di bawah adalah hasil uji nyata pada Node v24, bukan perkiraan.

Satu Tipe untuk Semua Angka #

Tidak ada tipe terpisah untuk integer dan desimal di JavaScript. 42 dan 3.14 sama-sama bertipe number, keduanya bisa dijumlahkan, dibandingkan, dan dipakai saling menggantikan. Operasi aritmetika dasar tersedia lewat operator +, -, *, dan / — operator-operator itu kita bedah tuntas di section Operators, jadi di sini cukup dipakai seperlunya:

js
console.log(18000 * 3);   // 54000
console.log(18000 / 4);   // 4500 — hasil pembagian juga number
console.log(10 + 3.5);    // 13.5 — bulat dan desimal bercampur bebas

Output yang Dihasilkan:

text
54000
4500
13.5

Di balik layar, setiap number disimpan sebagai angka floating point 64-bit standar IEEE-754. Format ini mampu menampung rentang nilai yang sangat lebar, tetapi menuntut bayaran pada dua titik: presisi integer terbatas di atas batas tertentu, dan banyak pecahan desimal tidak bisa disimpan dengan tepat. Dua batasan inilah yang kita buktikan berikutnya.

Batas Aman Integer #

JavaScript menjamin presisi penuh untuk integer sampai Number.MAX_SAFE_INTEGER, yaitu 9007199254740991 — sekitar sembilan kuadriliun. Di atas batas itu, hasil perhitungan bisa meleset tanpa pesan error apa pun:

js
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(9007199254740991 + 1);    // masih tepat
console.log(9007199254740991 + 2);    // meleset: hasilnya sama dengan +1
console.log(9007199254740993);        // literal di luar batas ikut meleset

Output yang Dihasilkan:

text
9007199254740991
9007199254740992
9007199254740992
9007199254740992

Baris ketiga adalah bukti paling penting: 9007199254740991 + 2 menghasilkan 9007199254740992, angka yang sama dengan hasil + 1 pada baris kedua. Engine tidak memunculkan error; engine diam-diam membulatkan ke angka terdekat yang masih bisa disimpan. Untuk aplikasi sehari-hari — harga, stok, penghitung — batas ini jauh di luar jangkauan. Angka melebihi batas baru relevan untuk ID berukuran besar, dan kebutuhan itulah yang melayani tipe bigint di chapter berikutnya.

Jebakan 0.1 + 0.2 #

Jebakan floating point paling terkenal di JavaScript terjadi justru pada angka kecil:

js
console.log(0.1 + 0.2);         // bukan 0.3
console.log(0.1 + 0.2 === 0.3); // false

Output yang Dihasilkan:

text
0.30000000000000004
false

Penyebabnya ada pada format penyimpanan, bukan pada operator. Format IEEE-754 menyimpan angka dalam basis dua (biner), dan pecahan seperti 0.1 tidak bisa dinyatakan tepat dalam basis dua — sama seperti 1/3 tidak bisa dinyatakan tepat dalam desimal (0.3333... tanpa ujung). Engine menyimpan 0.1 dan 0.2 sebagai nilai terdekat yang muat, dan penjumlahan dua nilai hampir-tepat itu menghasilkan 0.30000000000000004.

Perilaku ini berlaku di semua bahasa yang memakai IEEE-754, bukan cacat JavaScript semata. Yang penting bagi kita: jangan pernah membandingkan hasil perhitungan desimal dengan === secara langsung.

Membulatkan dengan toFixed dan toPrecision #

Method toFixed(jumlahDesimal) membulatkan angka ke sejumlah tempat desimal. Method ini sering dipakai untuk menampilkan hasil perhitungan uang dengan rapi:

js
console.log((0.1 + 0.2).toFixed(2)); // "0.30"
console.log((0.1 + 0.2).toFixed(2) === "0.30");
console.log((1234.567).toFixed(2));  // "1234.57"

Output yang Dihasilkan:

text
0.30
true
1234.57

Perhatikan baris kedua: perbandingan dengan string "0.30" menghasilkan true. Itu petunjuk penting — toFixed() mengembalikan string, bukan number. Tanda kutip tidak tercetak oleh console.log, sehingga keluarannya terlihat seperti angka; padahal tipe hasilnya sudah berubah. Jika hasilnya akan dihitung lagi, konversi dulu dengan Number() — cara konversi kita latih di chapter Type Conversion.

Method sejenis, toPrecision(n), membulatkan ke sejumlah angka penting (significant figures) sejak digit pertama, bukan sejak titik desimal:

js
console.log((1234.5678).toPrecision(6)); // "1234.57" — 6 angka penting
console.log((123456).toPrecision(2));    // "1.2e+5" — notasi ilmiah

Output yang Dihasilkan:

text
1234.57
1.2e+5

Baris kedua menunjukkan konsekuensi yang mengejutkan: ketika jumlah angka penting lebih sedikit daripada digit angka aslinya, hasilnya berpindah ke notasi ilmiah (1.2e+5 berarti 1,2 × 10⁵). Keduanya sama-sama mengembalikan string — gunakan untuk keperluan tampilan, bukan perhitungan lanjutan.

Infinity dan NaN #

Dua nilai istimewa melengkapi tipe number. Infinity muncul ketika hasil perhitungan melampaui rentang yang bisa disimpan, sedangkan NaN (Not a Number) muncul ketika perhitungan angka menghasilkan sesuatu yang tidak bermakna:

js
console.log(1 / 0);    // Infinity
console.log(-1 / 0);   // -Infinity
console.log(0 / 0);    // NaN
console.log(NaN === NaN); // false

Output yang Dihasilkan:

text
Infinity
-Infinity
NaN
false

Pembagian dengan nol tidak menghentikan program: 1 / 0 menghasilkan Infinity, dan -1 / 0 menghasilkan -Infinity. Hanya 0 / 0 yang jatuh ke NaN karena hasilnya tidak terdefinisi. NaN juga muncul dari konversi teks yang gagal, misalnya Number("bukan angka").

Baris terakhir menyimpan kejutan: NaN === NaN menghasilkan false. Standar menetapkan NaN tidak sama dengan nilai apa pun — termasuk dirinya sendiri. Konsekuensinya, x === NaN tidak pernah bisa dipakai untuk mendeteksi NaN. Alat yang benar adalah method Number.isNaN():

js
console.log(Number.isNaN(0 / 0));                 // true
console.log(Number.isNaN(Number("bukan angka"))); // true
console.log(Number.isNaN("bukan angka"));         // false — ini string biasa

Output yang Dihasilkan:

text
true
true
false

Contoh Lengkap: Menguji Perilaku Angka #

Berkas berikut merangkum keempat eksperimen chapter ini dalam satu uji yang bisa dijalankan ulang kapan pun:

js uji-angka.js
// uji-angka.js
// Membuktikan perilaku tipe number: batas presisi, floating point, Infinity, dan NaN.

// --- Integer dijaga presisi sampai batas aman ---
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(9007199254740991 + 1);    // masih tepat
console.log(9007199254740991 + 2);    // meleset: hasilnya sama dengan +1
console.log(9007199254740993);        // literal di luar batas ikut meleset

// --- Jebakan floating point klasik ---
console.log(0.1 + 0.2);               // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);       // false

// --- Pembulatan untuk tampilan ---
console.log((0.1 + 0.2).toFixed(2));  // "0.30" — hasilnya string
console.log((0.1 + 0.2).toFixed(2) === "0.30");
console.log((1234.567).toFixed(2));   // 1234.57

// --- Infinity dan NaN ---
console.log(1 / 0);                   // Infinity
console.log(-1 / 0);                  // -Infinity
console.log(0 / 0);                   // NaN
console.log(NaN === NaN);             // false
console.log(Number.isNaN(0 / 0));     // true
console.log(Number.isNaN(Number("bukan angka"))); // true
console.log(Number.isNaN("bukan angka"));         // false — string biasa

Output yang Dihasilkan:

text
9007199254740991
9007199254740992
9007199254740992
9007199254740992
0.30000000000000004
false
0.30
true
1234.57
Infinity
-Infinity
NaN
false
true
true
false

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 5 (console.log(Number.MAX_SAFE_INTEGER);): konstanta bawaan Number menampilkan batas aman integer, 9007199254740991.
  2. Baris 6–8 (console.log(9007199254740991 + 1); dst.): +1 masih tepat, tetapi +2 menghasilkan angka yang sama, dan literal 9007199254740993 ikut dibulatkan menjadi 9007199254740992. Meleset terjadi tanpa error — di sinilah bigint dibutuhkan.
  3. Baris 11–12 (console.log(0.1 + 0.2); dst.): penjumlahan dua pecahan menghasilkan 0.30000000000000004, dan perbandingannya dengan 0.3 jatuh ke false.
  4. Baris 15–17 (console.log((0.1 + 0.2).toFixed(2)); dst.): toFixed(2) merapikan hasil menjadi "0.30" — perbandingan dengan string membuktikan hasilnya bertipe string, bukan number.
  5. Baris 20–22 (console.log(1 / 0); dst.): pembagian dengan nol menghasilkan Infinity atau -Infinity, sedangkan 0 / 0 jatuh ke NaN.
  6. Baris 23–26 (console.log(NaN === NaN); dst.): NaN tidak sama dengan dirinya sendiri; hanya Number.isNaN() yang dapat mendeteksinya dengan andal, dan method itu hanya mengaku true untuk NaN sungguhan — bukan untuk string biasa.

Dengan empat perilaku ini di kepala — batas aman, pecahan tidak tepat, pembulatan yang mengembalikan string, serta Infinity dan NaN — kita bisa membaca sebagian besar keluaran angka yang “aneh” tanpa panik. Ketika angka melebihi batas aman dan presisi benar-benar wajib, JavaScript menyediakan tipe khusus: bigint, yang kita bahas bersama Symbol di chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

uji-angka.js

Unduh berkas
javascript
// uji-angka.js
// Membuktikan perilaku tipe number: batas presisi, floating point, Infinity, dan NaN.

// --- Integer dijaga presisi sampai batas aman ---
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(9007199254740991 + 1);    // masih tepat
console.log(9007199254740991 + 2);    // meleset: hasilnya sama dengan +1
console.log(9007199254740993);        // literal di luar batas ikut meleset

// --- Jebakan floating point klasik ---
console.log(0.1 + 0.2);               // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);       // false

// --- Pembulatan untuk tampilan ---
console.log((0.1 + 0.2).toFixed(2));  // "0.30" — hasilnya string
console.log((0.1 + 0.2).toFixed(2) === "0.30");
console.log((1234.567).toFixed(2));   // 1234.57

// --- Infinity dan NaN ---
console.log(1 / 0);                   // Infinity
console.log(-1 / 0);                  // -Infinity
console.log(0 / 0);                   // NaN
console.log(NaN === NaN);             // false
console.log(Number.isNaN(0 / 0));     // true
console.log(Number.isNaN(Number("bukan angka"))); // true
console.log(Number.isNaN("bukan angka"));         // false — string biasa