Kuasai BigInt JavaScript untuk komputasi integer skala besar yang presisi. Pelajari sintaks, kasus penggunaan dalam kriptografi dan keuangan, serta atasi kendala umum seperti serialisasi JSON.
JavaScript BigInt: Panduan Komprehensif untuk Komputasi Angka Besar
Selama bertahun-tahun, developer JavaScript menghadapi batasan yang tersembunyi namun signifikan: kemampuan bawaan bahasa untuk menangani angka. Meskipun sangat cocok untuk perhitungan sehari-hari, tipe Number
JavaScript akan gagal saat dihadapkan dengan integer yang sangat besar yang diperlukan dalam bidang seperti kriptografi, komputasi ilmiah, dan sistem data modern. Hal ini menyebabkan munculnya berbagai solusi sementara, pustaka pihak ketiga, dan kesalahan presisi yang halus dan sulit dilacak.
Era itu telah berakhir. Pengenalan BigInt sebagai tipe primitif bawaan JavaScript telah merevolusi cara kita bekerja dengan angka besar. Ini menyediakan cara yang kuat, ergonomis, dan efisien untuk melakukan aritmetika integer dengan presisi sewenang-wenang, langsung di dalam bahasa.
Panduan komprehensif ini ditujukan untuk developer di seluruh dunia. Kita akan menyelami lebih dalam tentang "mengapa, apa, dan bagaimana" dari BigInt. Baik Anda sedang membangun aplikasi keuangan, berinteraksi dengan blockchain, atau hanya mencoba memahami mengapa ID unik berukuran besar dari API berperilaku aneh, artikel ini akan membekali Anda dengan pengetahuan untuk menguasai BigInt.
Masalahnya: Batasan Tipe Number JavaScript
Sebelum kita dapat menghargai solusinya, kita harus memahami sepenuhnya masalahnya. JavaScript hanya memiliki satu tipe angka selama sebagian besar sejarahnya: tipe Number
. Di balik layar, ia direpresentasikan sebagai angka floating-point 64-bit presisi ganda IEEE 754. Format ini sangat baik untuk merepresentasikan berbagai nilai, termasuk desimal, tetapi memiliki batasan kritis ketika menyangkut integer.
Mengenal MAX_SAFE_INTEGER
Karena representasi floating-point-nya, ada batas ukuran integer yang dapat direpresentasikan dengan presisi sempurna. Batas ini diekspos melalui konstanta: Number.MAX_SAFE_INTEGER
.
Nilainya adalah 253 - 1, yaitu 9.007.199.254.740.991. Mari kita sebut saja sembilan kuadriliun untuk singkatnya.
Setiap integer dalam rentang -Number.MAX_SAFE_INTEGER
hingga +Number.MAX_SAFE_INTEGER
dianggap sebagai "integer aman". Ini berarti ia dapat direpresentasikan secara tepat dan dibandingkan dengan benar. Tapi apa yang terjadi ketika kita melangkah keluar dari rentang ini?
Mari kita lihat aksinya:
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 9007199254740991
// Mari tambahkan 1
console.log(maxSafe + 1); // 9007199254740992 - Ini terlihat benar
// Mari tambahkan 1 lagi
console.log(maxSafe + 2); // 9007199254740992 - Waduh. Hasilnya salah.
// Makin parah
console.log(maxSafe + 3); // 9007199254740994 - Tunggu, apa?
console.log(maxSafe + 4); // 9007199254740996 - Angkanya loncat!
// Pengecekan kesetaraan juga gagal
console.log(maxSafe + 1 === maxSafe + 2); // true - Ini salah secara matematis!
Seperti yang Anda lihat, begitu kita melebihi Number.MAX_SAFE_INTEGER
, JavaScript tidak lagi dapat menjamin presisi perhitungan kita. Representasi angka mulai memiliki celah, yang mengarah pada kesalahan pembulatan dan hasil yang salah. Ini adalah mimpi buruk bagi aplikasi yang menuntut akurasi dengan integer besar.
Solusi Sementara yang Lama
Selama bertahun-tahun, komunitas developer global mengandalkan pustaka eksternal untuk menyelesaikan masalah ini. Pustaka seperti bignumber.js
, decimal.js
, dan long.js
menjadi alat standar. Mereka bekerja dengan merepresentasikan angka besar sebagai string atau array digit dan mengimplementasikan operasi aritmetika di perangkat lunak.
Meskipun efektif, pustaka ini memiliki kelemahan:
- Overhead Performa: Operasi jauh lebih lambat daripada kalkulasi angka bawaan.
- Ukuran Bundle: Menambah bobot pada bundle aplikasi, sebuah kekhawatiran untuk performa web.
- Sintaks Berbeda: Developer harus menggunakan metode objek (mis.,
a.add(b)
) alih-alih operator aritmetika standar (a + b
), membuat kode kurang intuitif.
Memperkenalkan BigInt: Solusi Bawaan
BigInt diperkenalkan di ES2020 untuk menyelesaikan masalah ini secara bawaan. BigInt
adalah tipe primitif baru di JavaScript yang menyediakan cara untuk merepresentasikan bilangan bulat yang lebih besar dari 253 - 1.
Fitur utama BigInt adalah ukurannya tidak tetap. Ia dapat merepresentasikan integer yang besarnya sewenang-wenang, hanya dibatasi oleh memori yang tersedia di sistem host. Ini sepenuhnya menghilangkan masalah presisi yang kita lihat pada tipe Number
.
Cara Membuat BigInt
Ada dua cara utama untuk membuat BigInt:
- Menambahkan `n` pada literal integer: Ini adalah metode yang paling sederhana dan paling umum.
- Menggunakan fungsi konstruktor `BigInt()`: Ini berguna saat mengonversi nilai dari tipe lain, seperti string atau angka.
Berikut tampilannya dalam kode:
// 1. Menggunakan akhiran 'n'
const myFirstBigInt = 900719925474099199n;
const anotherBigInt = 123456789012345678901234567890n;
// 2. Menggunakan konstruktor BigInt()
const fromString = BigInt("98765432109876543210");
const fromNumber = BigInt(100);
// Anda dapat memeriksa tipenya
console.log(typeof myFirstBigInt); // "bigint"
console.log(typeof 100); // "number"
Dengan BigInt, perhitungan kita yang sebelumnya gagal sekarang berfungsi dengan sempurna:
const maxSafePlusOne = BigInt(Number.MAX_SAFE_INTEGER) + 1n;
const maxSafePlusTwo = BigInt(Number.MAX_SAFE_INTEGER) + 2n;
console.log(maxSafePlusOne.toString()); // "9007199254740992"
console.log(maxSafePlusTwo.toString()); // "9007199254740993"
// Kesetaraan berfungsi seperti yang diharapkan
console.log(maxSafePlusOne === maxSafePlusTwo); // false
Bekerja dengan BigInt: Sintaks dan Operasi
BigInt berperilaku sangat mirip dengan angka biasa, tetapi dengan beberapa perbedaan krusial yang harus dipahami setiap developer untuk menghindari bug.
Operasi Aritmetika
Semua operator aritmetika standar berfungsi dengan BigInt:
- Penjumlahan:
+
- Pengurangan:
-
- Perkalian:
*
- Perpangkatan:
**
- Modulus (Sisa Bagi):
%
Satu operator yang berperilaku berbeda adalah pembagian (`/`).
const a = 10n;
const b = 3n;
console.log(a + b); // 13n
console.log(a - b); // 7n
console.log(a * b); // 30n
console.log(a ** b); // 1000n
console.log(a % b); // 1n
Peringatan tentang Pembagian
Karena BigInt hanya dapat merepresentasikan bilangan bulat, hasil pembagian selalu dipotong (bagian pecahan dibuang). Hasilnya tidak dibulatkan.
const a = 10n;
const b = 3n;
console.log(a / b); // 3n (bukan 3.333...n)
const c = 9n;
const d = 10n;
console.log(c / d); // 0n
Ini adalah perbedaan yang krusial. Jika Anda perlu melakukan perhitungan dengan desimal, BigInt bukanlah alat yang tepat. Anda harus terus menggunakan Number
atau pustaka desimal khusus.
Perbandingan dan Kesetaraan
Operator perbandingan seperti >
, <
, >=
, dan <=
bekerja dengan mulus antara BigInt, dan bahkan antara BigInt dan Number.
console.log(10n > 5); // true
console.log(10n < 20); // true
console.log(10n > 20n); // false
Namun, kesetaraan lebih bernuansa dan merupakan sumber kebingungan yang umum.
- Kesetaraan Longgar (`==`): Operator ini melakukan pemaksaan tipe (type coercion). Ia menganggap BigInt dan Number dengan nilai matematis yang sama sebagai sama.
- Kesetaraan Ketat (`===`): Operator ini tidak melakukan pemaksaan tipe. Karena BigInt dan Number adalah tipe yang berbeda, ia akan selalu mengembalikan
false
saat membandingkannya.
console.log(10n == 10); // true - Hati-hati dengan ini!
console.log(10n === 10); // false - Direkomendasikan untuk kejelasan.
console.log(0n == 0); // true
console.log(0n === 0); // false
Praktik Terbaik: Untuk menghindari bug yang halus, selalu gunakan kesetaraan ketat (`===`) dan bersikap eksplisit tentang tipe yang Anda bandingkan. Jika Anda perlu membandingkan BigInt dan Number, seringkali lebih jelas untuk mengonversi salah satunya terlebih dahulu, dengan tetap mengingat potensi kehilangan presisi.
Ketidakcocokan Tipe: Pemisahan yang Ketat
JavaScript memberlakukan aturan ketat: Anda tidak dapat mencampur operan BigInt dan Number di sebagian besar operasi aritmetika.
Mencoba melakukannya akan menghasilkan TypeError
. Ini adalah pilihan desain yang disengaja untuk mencegah developer kehilangan presisi secara tidak sengaja.
const myBigInt = 100n;
const myNumber = 50;
try {
const result = myBigInt + myNumber; // Ini akan menghasilkan error
} catch (error) {
console.log(error); // TypeError: Cannot mix BigInt and other types, use explicit conversions
}
Pendekatan yang Benar: Konversi Eksplisit
Untuk melakukan operasi antara BigInt dan Number, Anda harus secara eksplisit mengonversi salah satunya.
const myBigInt = 100n;
const myNumber = 50;
// Konversi Number ke BigInt (aman)
const result1 = myBigInt + BigInt(myNumber);
console.log(result1); // 150n
// Konversi BigInt ke Number (berpotensi tidak aman!)
const veryLargeBigInt = 900719925474099199n;
// Ini akan kehilangan presisi!
const unsafeNumber = Number(veryLargeBigInt);
console.log(unsafeNumber); // 900719925474099200 - Nilainya telah dibulatkan!
const safeResult = Number(100n) + myNumber;
console.log(safeResult); // 150
Aturan Kritis: Hanya konversi BigInt ke Number jika Anda benar-benar yakin nilainya pas dalam rentang integer aman. Jika tidak, selalu konversi Number ke BigInt untuk menjaga presisi.
Kasus Penggunaan Praktis untuk BigInt dalam Konteks Global
Kebutuhan akan BigInt bukanlah masalah akademis yang abstrak. Ini memecahkan tantangan dunia nyata yang dihadapi oleh developer di berbagai domain internasional.
1. Timestamp Presisi Tinggi
`Date.now()` JavaScript mengembalikan jumlah milidetik sejak epoch Unix. Meskipun cukup untuk banyak aplikasi web, ini tidak cukup granular untuk sistem berkinerja tinggi. Banyak sistem terdistribusi, database, dan kerangka kerja logging di seluruh dunia menggunakan timestamp dengan presisi nanodetik untuk mengurutkan peristiwa secara akurat. Timestamp ini sering direpresentasikan sebagai integer 64-bit, yang terlalu besar untuk tipe `Number`.
// Timestamp dari sistem resolusi tinggi (mis., dalam nanodetik)
const nanoTimestampStr = "1670000000123456789";
// Menggunakan Number menghasilkan kehilangan presisi
const lostPrecision = Number(nanoTimestampStr);
console.log(lostPrecision); // 1670000000123456800 - Salah!
// Menggunakan BigInt menyimpannya dengan sempurna
const correctTimestamp = BigInt(nanoTimestampStr);
console.log(correctTimestamp.toString()); // "1670000000123456789"
// Sekarang kita dapat melakukan perhitungan yang akurat
const oneSecondInNanos = 1_000_000_000n;
const nextSecond = correctTimestamp + oneSecondInNanos;
console.log(nextSecond.toString()); // "1670001000123456789"
2. Pengidentifikasi Unik (ID) dari API
Skenario yang sangat umum adalah berinteraksi dengan API yang menggunakan integer 64-bit untuk ID objek unik. Ini adalah pola yang digunakan oleh platform global besar seperti Twitter (ID Snowflake) dan banyak sistem database (mis., tipe `BIGINT` di SQL).
Saat Anda mengambil data dari API semacam itu, parser JSON di browser atau lingkungan Node.js Anda mungkin mencoba mengurai ID besar ini sebagai `Number`, yang menyebabkan kerusakan data bahkan sebelum Anda sempat bekerja dengannya.
// Respons JSON tipikal dari sebuah API
// Catatan: ID adalah angka besar, bukan string.
const jsonResponse = '{"id": 1367874743838343168, "text": "Hello, world!"}';
// JSON.parse standar akan merusak ID
const parsedData = JSON.parse(jsonResponse);
console.log(parsedData.id); // 1367874743838343200 - ID salah!
// Solusi: Pastikan API mengirim ID besar sebagai string.
const safeJsonResponse = '{"id": "1367874743838343168", "text": "Hello, world!"}';
const safeParsedData = JSON.parse(safeJsonResponse);
const userId = BigInt(safeParsedData.id);
console.log(userId); // 1367874743838343168n - Benar!
Inilah mengapa menjadi praktik terbaik yang diterima secara luas bagi API di seluruh dunia untuk melakukan serialisasi ID integer besar sebagai string dalam payload JSON untuk memastikan kompatibilitas dengan semua klien.
3. Kriptografi
Kriptografi modern dibangun di atas matematika yang melibatkan integer yang sangat besar. Algoritma seperti RSA bergantung pada operasi dengan angka yang panjangnya ratusan atau bahkan ribuan bit. BigInt memungkinkan untuk melakukan perhitungan ini secara bawaan di JavaScript, yang penting untuk aplikasi kriptografi berbasis web, seperti yang menggunakan Web Crypto API atau mengimplementasikan protokol di Node.js.
Meskipun contoh kriptografi lengkap itu kompleks, kita bisa melihat demonstrasi konseptualnya:
// Dua bilangan prima yang sangat besar (hanya untuk tujuan demonstrasi)
const p = 1143400375533529n;
const q = 982451653n; // Yang lebih kecil untuk contoh ini
// Dalam RSA, Anda mengalikannya untuk mendapatkan modulus
const n = p * q;
console.log(n.toString()); // "1123281328905333100311297"
// Perhitungan ini tidak mungkin dilakukan dengan tipe Number.
// BigInt menanganinya dengan mudah.
4. Aplikasi Keuangan dan Blockchain
Saat berurusan dengan keuangan, terutama dalam konteks mata uang kripto, presisi adalah yang terpenting. Banyak mata uang kripto, seperti Bitcoin, mengukur nilai dalam unit terkecilnya (mis., satoshi). Jumlah total unit ini dapat dengan mudah melebihi `Number.MAX_SAFE_INTEGER`. BigInt adalah alat yang sempurna untuk menangani jumlah besar dan presisi ini tanpa menggunakan aritmetika floating-point, yang rentan terhadap kesalahan pembulatan.
// 1 Bitcoin = 100.000.000 satoshi
const satoshisPerBTC = 100_000_000n;
// Total pasokan Bitcoin adalah 21 juta
const totalBTCSupply = 21_000_000n;
// Hitung total satoshi
const totalSatoshis = totalBTCSupply * satoshisPerBTC;
// 2.100.000.000.000.000 - Ini adalah 2,1 kuadriliun
console.log(totalSatoshis.toString());
// Nilai ini lebih besar dari Number.MAX_SAFE_INTEGER
console.log(totalSatoshis > BigInt(Number.MAX_SAFE_INTEGER)); // true
Topik Lanjutan dan Kendala Umum
Serialisasi dan JSON.stringify()
Salah satu masalah paling umum yang dihadapi developer adalah melakukan serialisasi objek yang berisi BigInt. Secara default, `JSON.stringify()` tidak tahu cara menangani tipe `bigint` dan akan menghasilkan `TypeError`.
const data = {
id: 12345678901234567890n,
user: 'alex'
};
try {
JSON.stringify(data);
} catch (error) {
console.log(error); // TypeError: Do not know how to serialize a BigInt
}
Solusi 1: Implementasikan metode `toJSON`
Anda dapat memberi tahu `JSON.stringify` cara menangani BigInt dengan menambahkan metode `toJSON` ke `BigInt.prototype`. Pendekatan ini menambal prototipe global, yang mungkin tidak diinginkan di beberapa lingkungan bersama, tetapi sangat efektif.
// Patch global. Gunakan dengan pertimbangan.
BigInt.prototype.toJSON = function() {
return this.toString();
};
const data = { id: 12345678901234567890n, user: 'alex' };
const jsonString = JSON.stringify(data);
console.log(jsonString); // '{"id":"12345678901234567890","user":"alex"}'
Solusi 2: Gunakan fungsi replacer
Pendekatan yang lebih aman dan lebih terlokalisasi adalah dengan menggunakan argumen `replacer` di `JSON.stringify`. Fungsi ini dipanggil untuk setiap pasangan kunci/nilai dan memungkinkan Anda untuk mengubah nilai sebelum serialisasi.
const data = { id: 12345678901234567890n, user: 'alex' };
const replacer = (key, value) => {
if (typeof value === 'bigint') {
return value.toString();
}
return value;
};
const jsonString = JSON.stringify(data, replacer);
console.log(jsonString); // '{"id":"12345678901234567890","user":"alex"}'
Operasi Bitwise
BigInt mendukung semua operator bitwise yang Anda kenal dari tipe `Number`: `&` (AND), `|` (OR), `^` (XOR), `~` (NOT), `<<` (left shift), dan `>>` (sign-propagating right shift). Ini sangat berguna saat bekerja dengan format data tingkat rendah, perizinan, atau jenis algoritma tertentu.
const permissions = 5n; // 0101 dalam biner
const READ_PERMISSION = 4n; // 0100
const WRITE_PERMISSION = 2n; // 0010
// Periksa apakah izin baca diatur
console.log((permissions & READ_PERMISSION) > 0n); // true
// Periksa apakah izin tulis diatur
console.log((permissions & WRITE_PERMISSION) > 0n); // false
// Tambahkan izin tulis
const newPermissions = permissions | WRITE_PERMISSION;
console.log(newPermissions); // 7n (yaitu 0111)
Pertimbangan Performa
Meskipun BigInt sangat kuat, penting untuk memahami karakteristik performanya:
- Number vs. BigInt: Untuk integer dalam rentang aman, operasi `Number` standar jauh lebih cepat. Ini karena mereka sering dapat dipetakan langsung ke instruksi tingkat mesin yang diproses oleh CPU komputer. Operasi BigInt, karena ukurannya yang sewenang-wenang, memerlukan algoritma berbasis perangkat lunak yang lebih kompleks.
- BigInt vs. Pustaka: `BigInt` bawaan umumnya jauh lebih cepat daripada pustaka angka besar berbasis JavaScript. Implementasinya adalah bagian dari mesin JavaScript (seperti V8 atau SpiderMonkey) dan ditulis dalam bahasa tingkat lebih rendah seperti C++, memberinya keunggulan performa yang signifikan.
Aturan Emas: Gunakan `Number` untuk semua perhitungan numerik kecuali Anda memiliki alasan spesifik untuk percaya bahwa nilainya mungkin melebihi `Number.MAX_SAFE_INTEGER`. Gunakan `BigInt` saat Anda membutuhkan kemampuannya, bukan sebagai pengganti default untuk semua angka.
Kompatibilitas Browser dan Lingkungan
BigInt adalah fitur JavaScript modern, tetapi dukungannya sekarang sudah tersebar luas di seluruh ekosistem global.
- Browser Web: Didukung di semua browser modern utama (Chrome 67+, Firefox 68+, Safari 14+, Edge 79+).
- Node.js: Didukung sejak versi 10.4.0.
Untuk proyek yang perlu mendukung lingkungan yang sangat tua, transpilasi menggunakan alat seperti Babel bisa menjadi pilihan, tetapi ini disertai dengan penalti performa. Mengingat dukungan yang luas saat ini, sebagian besar proyek baru dapat menggunakan BigInt secara bawaan tanpa khawatir.
Kesimpulan dan Praktik Terbaik
BigInt adalah tambahan yang kuat dan penting untuk bahasa JavaScript. Ini menyediakan solusi bawaan, efisien, dan ergonomis untuk masalah aritmetika integer besar yang sudah lama ada, memungkinkan kelas aplikasi baru untuk dibangun dengan JavaScript, dari kriptografi hingga penanganan data presisi tinggi.
Untuk menggunakannya secara efektif dan menghindari kendala umum, ingatlah praktik terbaik ini:
- Gunakan Akhiran `n`: Lebih suka sintaks literal `123n` untuk membuat BigInt. Ini jelas, ringkas, dan menghindari potensi kehilangan presisi saat pembuatan.
- Jangan Campur Tipe: Ingat bahwa Anda tidak dapat mencampur BigInt dan Number dalam operasi aritmetika. Bersikaplah eksplisit dengan konversi Anda: `BigInt()` atau `Number()`.
- Prioritaskan Presisi: Saat mengonversi antar tipe, selalu utamakan konversi `Number` ke `BigInt` untuk mencegah kehilangan presisi yang tidak disengaja.
- Gunakan Kesetaraan Ketat: Gunakan `===` alih-alih `==` untuk perbandingan guna menghindari perilaku membingungkan yang disebabkan oleh pemaksaan tipe.
- Tangani Serialisasi JSON: Rencanakan untuk serialisasi BigInt. Gunakan fungsi `replacer` kustom di `JSON.stringify` untuk solusi yang aman dan non-global.
- Pilih Alat yang Tepat: Gunakan `Number` untuk matematika tujuan umum dalam rentang integer aman untuk performa yang lebih baik. Gunakan `BigInt` hanya ketika Anda benar-benar membutuhkan kemampuan presisi sewenang-wenangnya.
Dengan merangkul BigInt dan memahami aturannya, Anda dapat menulis aplikasi JavaScript yang lebih kuat, akurat, dan berdaya yang mampu mengatasi tantangan numerik dalam skala apa pun.