Float64 menyimpan sekitar 15-16 angka signifikan, sedangkan float32 hanya sekitar 7-8. Hemat memori setengahnya membuat float32 menarik untuk data besar, tetapi presisi yang lebih pendek memunculkan efek yang tidak terlihat pada array kecil: penjumlahan jutaan nilai bisa meleset terlihat jelas. Bab ini mengamati efek tersebut secara langsung dan menutup section dengan praktik menjaga akurasi.
float64 vs float32 dalam Akumulasi Besar #
Kita jumlahkan satu juta salinan nilai 0.1 — nilai yang sebenarnya tidak dapat direpresentasikan persis dalam biner — dengan tiga cara: di akumulasi float64, di akumulasi float32, serta nilai float32 yang diakumulasi kembali dalam float64:
import numpy as np
data = np.full(1_000_000, 0.1)
print(np.sum(data))
print(np.sum(data, dtype=np.float32))
data32 = data.astype(np.float32)
print(np.sum(data32, dtype=np.float64))Output yang Dihasilkan:
99999.9999999998
100000.086
100000.00149011612Mari kita bedah ketiga hasil:
np.sum(data): array float64 dengan akumulasi float64 menghasilkan99999.9999999998— meleset sekitar 2x10^-10 dari satu juta, tingkat kesalahan yang wajar untuk presisi float64.np.sum(data, dtype=np.float32): parameterdtypememaksa akumulasi dilakukan dalam float32; kesalahan pembulatan menumpuk hingga100000.086, meleset 0.086 dari nilai sebenarnya.np.sum(data32, dtype=np.float64): array float32 diakumulasi dalam float64, sehingga hasilnya memperlihatkan penyimpangan murni dari konversi 0.1 ke float32:100000.00149011612.
Tabel berikut merangkum perbedaan kedua tipe:
| Aspek | float32 | float64 |
|---|---|---|
| Jumlah bit | 32 | 64 |
| Angka signifikan (kira-kira) | 7-8 | 15-16 |
| Hasil jumlah 1 juta x 0.1 | 100000.086 | 99999.9999999998 |
| Penggunaan lazim | Grafika, model ML, citra | Perhitungan ilmiah dan keuangan |
Urutan Penjumlahan Juga Memengaruhi Hasil #
Presisi bukan satu-satunya faktor; urutan operasi ikut menentukan. Penjumlahan berurutan kiri-ke-kanan menumpuk galat sepanjang jalan, sedangkan np.sum memakai algoritma pairwise summation yang mengelompokkan penjumlahan menjadi blok-blok kecil:
import numpy as np
data = np.full(10, 0.1)
total = 0.0
for v in data.tolist():
total += v
print(total)
print(np.sum(data))Output yang Dihasilkan:
0.9999999999999999
1.0Loop menjumlahkan sepuluh nilai 0.1 satu per satu dan berakhir di 0.9999999999999999, sementara np.sum pada data yang sama menghasilkan 1.0 persis. Pada array besar, keunggulan pairwise summation inilah yang membuat np.sum lebih akurat dibanding akumulasi berurutan manual.
Menampilkan Hasil: repr vs Format #
Nilai hasil perhitungan dan tampilannya adalah dua hal berbeda. print menampilkan representasi terpendek yang masih merekonstruksi nilai float64 secara persis, sementara format f-string bisa menutupi atau justru membuka penyimpangan:
import numpy as np
total = np.sum(np.full(1_000_000, 0.1))
print(total)
print(f"{total:.2f}")
print(f"{total:.15f}")Output yang Dihasilkan:
99999.9999999998
100000.00
99999.999999999796273Format :.2f menampilkan 100000.00 — tampilan yang rapi untuk laporan, tetapi menyembunyikan selisih dari angka bulat. Sebaliknya :.15f membuka nilai penuh 99999.999999999796273. Praktiknya: format hanya pada lapisan tampilan, dan biarkan nilai mentah tetap dipakai untuk perhitungan lanjutan.
Kapan float32 Cukup, Kapan Menjadi Risiko #
float32 bukan pilihan yang salah; pilihannya bergantung pada jenis perhitungan:
- Aman memakai float32: piksel dan tekstur grafika, bobot model machine learning yang dipakai untuk inferensi, sinyal per blok pendek, dan data citra — rentang nilainya terkendali dan galat kecil tidak mengumpul.
- Risiko memakai float32: akumulasi keuangan (total tagihan jutaan transaksi), penjumlahan berjalan (running total) sepanjang waktu, selisih dua nilai yang hampir sama, dan data yang terus ditambahkan tanpa reset.
Panduan praktis menjaga akurasi:
- Untuk akumulasi besar, simpan data dalam float32 bila memori terbatas tetapi akumulasi dengan
dtype=np.float64. - Bandingkan bilangan pecahan dengan
np.iscloseataunp.allclose, bukan dengan==. - Format angka hanya saat menampilkan; jangan membulatkan data yang masih dipakai untuk menghitung.
- Saat mengurangi dua nilai yang hampir sama, pertimbangkan menghitung selisihnya di float64 sejak awal.
Dengan kebiasaan ini, presisi terbatas menjadi pertukaran yang terkendali, bukan sumber hasil yang mengejutkan.