MATLAB menyimpan setiap angka pecahan sebagai floating point standar IEEE 754, bukan sebagai nilai desimal eksak. Akibatnya, 0.1 + 0.2 di MATLAB tidak sama persis dengan 0.3, dan penjumlahan sepuluh ribu angka bisa menyimpang dari hasil teoretisnya walau kodenya benar. Memahami cara angka disimpan membantu kita membaca hasil numerik secara kritis: kapan selisih kecil boleh diabaikan, dan kapan selisih kecil itu menandakan rumus yang keliru. Chapter ini membongkar struktur bit double precision, konstanta batas eps/realmin/realmax, kasus khusus Inf dan NaN, hingga akumulasi galat pada penjumlahan berulang.

Representasi IEEE 754 Double Precision #

Aturannya: setiap angka bertipe double menempati 64 bit yang terbagi menjadi tiga medan — sign (tanda), exponent (eksponen), dan mantissa (pecahan). Mekanisme di balik layar: MATLAB menyimpan angka dalam bentuk biner (-1)^s * 2^(e-1023) * 1.mantissa, sehingga mantissa 52 bit hanya mampu memuat sekitar 15-16 digit desimal signifikan. Angka desimal seperti 0.1 memiliki bentuk biner yang periodik (tak berujung), sehingga MATLAB harus membulatkannya ke double terdekat.

Diagram di atas memperlihatkan tata letak 64 bit: tanda menentukan positif atau negatif, eksponen menggeser titik biner (rentang kira-kira 2^-1022 sampai 2^1023), dan mantissa menyimpan digit signifikan. Fungsi num2hex() menampilkan isi bit tersebut dalam bentuk heksadesimal:

matlab
fprintf('num2hex(0.1)     = %s\n', num2hex(0.1));
fprintf('num2hex(0.1+0.2) = %s\n', num2hex(0.1 + 0.2));
fprintf('num2hex(0.3)     = %s\n', num2hex(0.3));
fprintf('0.1 + 0.2 - 0.3  = %.6e\n', 0.1 + 0.2 - 0.3);

Output yang Dihasilkan:

text
num2hex(0.1)     = 3fb999999999999a
num2hex(0.1+0.2) = 3fd3333333333334
num2hex(0.3)     = 3fd3333333333333
0.1 + 0.2 - 0.3  = 5.551115e-17

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (num2hex(0.1)): pola 999...99a pada mantissa menandakan 0.1 memang tidak bisa ditulis eksak dalam biner — nilainya dibulatkan ke double terdekat.
  2. Baris 2 (num2hex(0.1+0.2)): hasil penjumlahan berakhiran digit ...34.
  3. Baris 3 (num2hex(0.3)): representasi 0.3 langsung berakhiran ...33 — berbeda satu langkah pembulatan (1 ulp) dari hasil penjumlahan.
  4. Baris 4 (0.1 + 0.2 - 0.3): selisih 5.55e-17 adalah bukti bahwa 0.1 + 0.2 tidak identik dengan 0.3 di level bit.

Konstanta Batas: realmin, realmax, dan eps #

MATLAB menyediakan tiga konstanta bawaan yang memetakan batas representasi double. Eksponen 11 bit membatasi rentang bilangan, sedangkan mantissa 52 bit membatasi ketelitian:

matlab
fprintf('eps     = %.6e\n', eps);
fprintf('realmin = %.6e\n', realmin);
fprintf('realmax = %.6e\n', realmax);
fprintf('eps(1)  = %.6e\n', eps(1));
fprintf('eps(2)  = %.6e\n', eps(2));
fprintf('eps(16) = %.6e\n', eps(16));

Output yang Dihasilkan:

text
eps     = 2.220446e-16
realmin = 2.225074e-308
realmax = 1.797693e+308
eps(1)  = 2.220446e-16
eps(2)  = 4.440892e-16
eps(16) = 3.552714e-15

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (eps): jarak dari 1 ke double terdekat berikutnya — ukuran pembulatan dasar sekitar 2.22e-16.
  2. Baris 2-3 (realmin, realmax): bilangan normal positif terkecil dan terbesar yang bisa disimpan.
  3. Baris 4-6 (eps(x)): jarak antar double pada skala x; setiap kali nilai dua kali lebih besar, jaraknya juga dua kali lebih besar — itulah sebabnya eps(2) dan eps(16) lebih besar daripada eps(1).

Konsekuensi langsungnya: bilangan bulat hanya eksak sampai 2^53 (sekitar 9e15), karena di atas nilai tersebut jarak antar double melampaui 1.

matlab
fprintf('2^53 + 1 - 2^53 = %d\n', 2^53 + 1 - 2^53);
fprintf('2^53 + 2 - 2^53 = %d\n', 2^53 + 2 - 2^53);

Output yang Dihasilkan:

text
2^53 + 1 - 2^53 = 0
2^53 + 2 - 2^53 = 2

Pada nilai 2^53, jarak antar bilangan adalah 2. Penambahan 1 dibulatkan kembali ke 2^53 (hasil 0), sedangkan penambahan 2 tersimpan dengan benar.

Denormal, Inf, dan NaN #

Angka di bawah realmin tidak langsung jatuh ke nol. MATLAB memakai bilangan denormal: eksponen diturunkan ke nol dan presisi mantissa berkurang bertahap (gradual underflow) sampai bilangan terkecil realmin * eps = 4.94e-324. Sebaliknya, hasil yang melebihi realmax menjadi Inf (overflow), dan operasi tak terdefinisi menghasilkan NaN:

matlab
fprintf('realmin / 2   = %.6e (denormal)\n', realmin / 2);
fprintf('realmin * eps = %.6e (denormal terkecil)\n', realmin * eps);
fprintf('realmin*eps / 2 == 0 -> %d (underflow)\n', realmin * eps / 2 == 0);
fprintf('realmax * 2 = %g (overflow)\n', realmax * 2);
fprintf('1/0 = %g, 0/0 = %g, Inf - Inf = %g\n', 1/0, 0/0, Inf - Inf);
fprintf('NaN == NaN -> %d, NaN ~= NaN -> %d\n', NaN == NaN, NaN ~= NaN);

Output yang Dihasilkan:

text
realmin / 2   = 1.112537e-308 (denormal)
realmin * eps = 4.940656e-324 (denormal terkecil)
realmin*eps / 2 == 0 -> 1 (underflow)
realmax * 2 = Inf (overflow)
1/0 = Inf, 0/0 = NaN, Inf - Inf = NaN
NaN == NaN -> 0, NaN ~= NaN -> 1

Mari kita bedah baris-baris kode di atas:

  1. Baris 1-3: pembagian realmin/2 masih tersimpan sebagai denormal, tetapi setelah melewati realmin*eps, pembagian dua berikutnya menghasilkan 0 murni — underflow penuh.
  2. Baris 4-5: realmax*2 meluap menjadi Inf; pembagian 1/0 juga Inf, sedangkan 0/0 dan Inf-Inf tidak punya nilai yang bermakna sehingga menjadi NaN.
  3. Baris 6: perbandingan NaN == NaN bernilai false — NaN tidak pernah sama dengan apa pun, termasuk dirinya sendiri.

Akumulasi Galat pada Penjumlahan #

Masalah nyatanya: galat pembulatan tidak berhenti di satu operasi — galat menumpuk. Contoh klasiknya, kita menjumlahkan 0.1 sepuluh ribu kali dan membandingkan hasilnya dengan nilai eksak 1000:

matlab
total = 0;
for k = 1:10000
    total = total + 0.1;
end
fprintf('total = %.10f\n', total);
fprintf('galat = %.3e\n', total - 1000);

Output yang Dihasilkan:

text
total = 1000.0000000002
galat = 1.588e-10

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (total = 0): akumulator diinisialisasi sebelum loop — pola yang sama dengan loop penjumlahan di section struktur kontrol.
  2. Baris 2-4 (for loop): setiap iterasi membulatkan total + 0.1 ke double terdekat; pembulatan kecil yang berulang 10.000 kali menumpuk menjadi simpangan total 1.588e-10.
  3. Baris 5-6 (fprintf): pencetakan dengan 10 angka desimal menyingkap selisih yang tersembunyi — pada tampilan format short, kedua nilai tampak identik dengan 1000.

Simpangan 1.6e-10 terasa kecil, tetapi galat sejenis bisa membesar pada simulasi jutaan langkah. Kita bedah dampaknya yang lebih dramatis pada chapter Numerical Errors.

Penulisan Ilmiah #

Notasi ilmiah ditulis dengan huruf e: 1.5e3 berarti 1.5 x 10^3, dan 2.5e-4 berarti 2.5 x 10^-4. Bentuk ini bukan sekadar gaya tampilan — 6.02214076e23 adalah cara menulis konstanta fisika yang melebihi digit presisi double sekaligus menjaga keterbacaan:

matlab
fprintf('%g %g %g\n', 1.5e3, 2.5e-4, 6.02214076e23);

Output yang Dihasilkan:

text
1500 0.00025 6.02214e+23

Berkas contoh.m pada bagian bawah halaman menjalankan seluruh contoh di atas dalam satu script utuh, dari konstanta batas hingga akumulasi galat.

Ringkasan #

Setiap double MATLAB adalah 64 bit IEEE 754: 1 bit tanda, 11 bit eksponen, 52 bit mantissa — sekitar 15-16 digit signifikan. Konstanta eps, realmin, dan realmax memetakan batas presisi dan rentang; eps(x) mengukur jarak antar bilangan pada skala x. Di bawah realmin tersedia denormal hingga 4.94e-324, di atas realmax muncul Inf, dan operasi tak terdefinisi menghasilkan NaN yang tidak pernah sama dengan dirinya sendiri. Karena setiap operasi membulatkan, galat kecil seperti 0.1 + 0.2 - 0.3 wajar terjadi dan menumpuk pada penjumlahan panjang — selalu bandingkan hasil floating point dengan toleransi, bukan ==.