Sistem Bilangan

Hierarki bilangan asli hingga real dan alasan float tidak sama dengan bilangan real.

Daftar isi

Topik ini membahas himpunan bilangan yang mendasari hampir semua perhitungan pada perangkat lunak, mulai dari bilangan asli, bilangan bulat, bilangan rasional, hingga bilangan real. Pembahasan kemudian menghubungkan hierarki tersebut dengan tipe float dan menjelaskan mengapa float bukan salinan setia dari bilangan real. Setelah topik ini dipahami, struktur bilangan beserta batas representasinya di komputer dapat diidentifikasi dengan tepat, termasuk alasan munculnya nilai seperti 0.30000000000000004.

Hierarki himpunan bilangan#

Bilangan pada matematika tersusun sebagai himpunan yang saling memuat. Bilangan asli N={1,2,3,…}\mathbb{N} = \{1, 2, 3, \ldots\} adalah bilangan penghitungan yang digunakan untuk menyebut banyaknya objek. Bilangan bulat Z\mathbb{Z} melengkapinya dengan nilai nol dan nilai negatif, sehingga berbentuk {…,−2,−1,0,1,2,…}\{\ldots, -2, -1, 0, 1, 2, \ldots\}. Bilangan rasional Q\mathbb{Q} mencakup semua bilangan yang dapat ditulis sebagai pecahan pq\frac{p}{q} dengan pp dan qq bilangan bulat serta q≠0q \neq 0, misalnya −12-\frac{1}{2} dan 34\frac{3}{4}. Hierarki keempat himpunan tersebut dirangkum dalam relasi berikut.

N⊂Z⊂Q⊂R \mathbb{N} \subset \mathbb{Z} \subset \mathbb{Q} \subset \mathbb{R}

Bilangan real R\mathbb{R} adalah himpunan terluar karena di dalamnya masih ada bilangan irasional, yaitu bilangan yang tidak dapat ditulis sebagai pecahan pq\frac{p}{q}, contohnya 2\sqrt{2} dan π\pi. Pada garis bilangan, bilangan rasional dicirikan oleh desimal yang berhenti atau berulang secara teratur, sedangkan bilangan irasional memiliki perluasan desimal yang berlangsung tanpa pola berulang. Diagram berikut menampilkan susunan bertingkat tersebut beserta contoh anggotanya.

Bilangan real (ℝ)Bilangan rasional (ℚ)Bilangan bulat (ℤ)Bilangan asli (ℕ)1, 2, 3, …−3, 0, 7−1/2, 3/4√2, π
Hierarki himpunan bilangan dari asli hingga real

Setiap tingkat hierarki memiliki padanan yang lazim di kode. Bilangan asli dan bulat diwakili oleh tipe bilangan bulat seperti int, bilangan rasional dapat diwakili oleh tipe pecahan seperti Fraction, sedangkan bilangan real dihampiri oleh float. Padanan ini bekerja dengan baik selama batas masing-masing representasi dipahami, dan batas itulah yang dibahas pada bagian berikutnya.

Float sebagai hampiran bilangan real#

Float pada Python dan NumPy mengikuti standar IEEE 754 double precision yang menyimpan nilai dalam 64 bit yang terdiri atas satu bit tanda, sebelas bit eksponen, dan lima puluh dua bit fraksi. Dengan susunan tersebut, float hanya dapat menyimpan pecahan biner yang berhingga, sedangkan bilangan real mencakup bilangan yang perluasan binernya tak berhingga. Konsekuensinya, sebagian besar bilangan desimal hanya disimpan sebagai hampiran terdekat. Bilangan 0.10.1 contohnya tidak memiliki bentuk biner berhingga karena perluasan binernya berulang tanpa akhir, sebagaimana ditunjukkan berikut.

0.110=(0.000110011…)2 0.1_{10} = (0.000110011\ldots)_2

Komputer menyimpan nilai yang paling dekat dengan 0.10.1 dalam bentuk pecahan biner berhingga. Modul fractions pada Python dapat memperlihatkan pecahan yang benar-benar tersimpan di dalam float tersebut. Fungsi Fraction menerima float dan mengembalikan pecahan paling sederhana yang bernilai sama persis dengan float itu.

python Contoh: pecahan yang benar-benar tersimpan di dalam float
from fractions import Fraction

print(Fraction(0.1))
print(Fraction(0.5))
text Keluaran
3602879701896397/36028797018963968
1/2

Keluaran di atas menunjukkan dua perilaku yang berbeda. Nilai 0.50.5 tersimpan secara tepat karena 12\frac{1}{2} adalah pecahan biner yang berhingga, sedangkan 0.10.1 tersimpan sebagai pecahan 360287970189639736028797018963968\frac{3602879701896397}{36028797018963968} yang hanya menghampiri 110\frac{1}{10}. Selisihnya sangat kecil, tetapi selisih itu ikut terbawa ke setiap operasi berikutnya.

Batas representasi float#

Selain presisi yang terbatas, float juga memiliki batas rentang nilai dan batas ketepatan untuk bilangan bulat. Program berikut merangkum batas-batas itu secara langsung. Ketiganya dibaca dari atribut resmi modul sys, sehingga nilainya dapat diverifikasi ulang di lingkungan mana pun.

python batas_float.py
"""Batas representasi float IEEE 754 double precision."""
import sys

print("Nilai terbesar :", sys.float_info.max)
print("Epsilon mesin  :", sys.float_info.epsilon)
print("0.1 + 0.2      =", repr(0.1 + 0.2))
print("0.1 + 0.2 == 0.3  ->", 0.1 + 0.2 == 0.3)
print("2**53 == 2**53 + 1 ->", 2.0**53 == 2.0**53 + 1)
text Keluaran
Nilai terbesar : 1.7976931348623157e+308
Epsilon mesin  : 2.220446049250313e-16
0.1 + 0.2      = 0.30000000000000004
0.1 + 0.2 == 0.3  -> False
2**53 == 2**53 + 1 -> True

Tiga temuan pada keluaran tersebut penting untuk diingat. Pertama, nilai float terbesar adalah sekitar (2−2−52)×21023≈1.8×10308(2 - 2^{-52}) \times 2^{1023} \approx 1.8 \times 10^{308}, dan perhitungan yang melewati nilai itu berubah menjadi inf. Kedua, epsilon mesin 2.22×10−162.22 \times 10^{-16} adalah jarak relatif terkecil yang masih dapat dibedakan di sekitar nilai 1.0, sehingga penambahan nilai yang lebih kecil daripada epsilon sering tidak berdampak apa pun. Ketiga, bilangan bulat hanya tersimpan tepat sampai 2532^{53}, karena hanya itu jumlah bit yang tersedia untuk digit signifikan, sehingga 253+12^{53} + 1 mengalami pemotongan dan perbandingannya bernilai True padahal secara matematika salah.

Di mana hal ini ditemui dalam kode#

Masalah presisi ini bukan sekadar teori karena hampir setiap proyek perangkat lunak menyentuhnya. Pemrosesan uang lazim dihindarkan dari float dengan menyimpan nilai dalam satuan sen sebagai bilangan bulat, atau dengan tipe Decimal yang bekerja secara desimal. Penjumlahan jutaan nilai float pada perhitungan statistik atau machine learning menyebabkan selisih hampiran menumpuk menjadi kesalahan yang terlihat, sehingga algoritma numerik dirancang dengan memperhitungkan sifat ini. Fungsi math.fsum contohnya mengurangi penumpukan selisih ketika penjumlahan panjang tidak terhindarkan. Kasus yang paling sering ditemui adalah perbandingan kesamaan, dan NumPy menyediakan fungsi bantu untuk keperluan tersebut.

python Contoh: perbandingan float dengan toleransi
import numpy as np

a = 0.1 + 0.2
print(a == 0.3)
print(np.isclose(a, 0.3))
print(np.isclose(a, 0.3, atol=1e-17, rtol=0))
text Keluaran
False
True
False

Fungsi np.isclose menganggap dua nilai sama apabila selisihnya berada dalam toleransi, dengan toleransi relatif dan absolut yang dapat diatur. Pemanggilan ketiga pada contoh memperketat toleransi hingga 10−1710^{-17}, dan selisih hampiran 0.1+0.20.1 + 0.2 terhadap 0.30.3 yang besarnya sekitar 5.5×10−175.5 \times 10^{-17} kini dianggap tidak sama. Perilaku ini menegaskan bahwa toleransi adalah keputusan perancangan, bukan angka ajaib, dan nilai yang tepat bergantung pada besaran data yang diproses.

Ringkasan#

  • Bilangan tersusun dalam hierarki N⊂Z⊂Q⊂R\mathbb{N} \subset \mathbb{Z} \subset \mathbb{Q} \subset \mathbb{R}, dan bilangan rasional berupa pecahan pq\frac{p}{q} sedangkan bilangan real masih menambahkan bilangan irasional seperti 2\sqrt{2}.
  • Float IEEE 754 hanya menyimpan pecahan biner berhingga dengan 53 bit digit signifikan, sehingga sebagian besar bilangan desimal hanya berupa hampiran, misalnya 0.10.1 yang tersimpan sebagai 360287970189639736028797018963968\frac{3602879701896397}{36028797018963968}.
  • Batas praktis float adalah nilai terbesar sekitar 1.8×103081.8 \times 10^{308}, epsilon mesin 2.22×10−162.22 \times 10^{-16}, dan ketepatan bilangan bulat hanya sampai 2532^{53}.
  • Perbandingan kesamaan float sebaiknya dilakukan dengan toleransi seperti np.isclose, atau perhitungan dialihkan ke representasi tepat seperti int dan Fraction ketika presisi menentukan.

Referensi#