2.39. Bekerja dengan float¶
Bilangan floating-point terlihat seperti desimal biasa, tetapi beberapa perilakunya mengejutkan pembaca yang belum pernah menemukannya sebelumnya -- dan salah satu perilaku tersebut lebih terasa pada MicroPython dibandingkan Python desktop. Halaman ini mencakup apa yang diharapkan dan cara menulis kode float yang tidak berperilaku buruk secara diam-diam.
2.39.1. Presisi¶
Python float adalah bilangan floating-point biner IEEE 754. Pada sebagian besar build MicroPython, presisinya adalah single precision (32-bit), sedangkan CPython desktop menggunakan double precision (64-bit). Single precision membawa sekitar tujuh digit desimal akurasi; double membawa sekitar lima belas.
>>> 0.1 + 0.2
0.3000000
>>> 1.0 / 3.0
0.3333333
>>> 1e30 * 1e30
inf
Rentang yang dapat direpresentasikan juga lebih sempit di kedua ujungnya: angka yang lebih besar dari sekitar 3.4e38 dalam besaran meluap ke inf, dan angka yang lebih kecil dari sekitar 1.2e-38 dibulatkan ke nol.
2.39.2. Membandingkan float¶
Kesalahan paling umum adalah menguji kesetaraan dengan ==:
>>> 0.1 + 0.2 == 0.3
False
Kedua ekspresi terlihat seolah seharusnya sama, tetapi hasil dari 0.1 + 0.2 adalah nilai terdekat yang dapat direpresentasikan, yang tidak sama persis dengan 0.3. Gunakan pemeriksaan toleransi sebagai gantinya -- tanyakan apakah dua float cukup dekat daripada identik:
if abs(a - b) < 1e-6:
# close enough
...
Pilihan toleransi tergantung pada skala nilai. Toleransi tetap 1e-6 bekerja dengan baik ketika angka-angka berada di sekitar orde 1; toleransi relatif lebih baik ketika nilai bervariasi berdasarkan orde besaran.
math.isclose() menangani keduanya sekaligus:
from math import isclose
isclose(0.1 + 0.2, 0.3) # True
isclose(1.0e6 + 1, 1.0e6) # True (within default tolerance)
Dua argumen kata kunci mengontrol jenis "dekat" mana yang dihitung:
rel_tol-- toleransi relatif, default1e-9. Dua nilai cocok jika perbedaannya berada dalam fraksi ini dari yang lebih besar. Baik untuk perbandingan umum di seluruh skala.abs_tol-- toleransi absolut, default0. Dua nilai cocok jika perbedaannya berada dalam jumlah tetap ini.
math.isclose() mengembalikan True jika salah satu toleransi terpenuhi. Default-nya baik untuk sebagian besar pasangan bilangan bukan nol; jebakannya adalah ketika salah satu nilai bisa persis nol. Pemeriksaan toleransi-relatif menghasilkan "difference ≤ rel_tol × nilai terbesar", dan nilai terbesar adalah nol, sehingga pemeriksaan selalu gagal:
>>> isclose(0.0, 1e-12)
False
Pemeriksaan toleransi-absolut tidak memiliki masalah tersebut -- berikan abs_tol kapan pun nol adalah nilai yang mungkin Anda bandingkan:
>>> isclose(0.0, 1e-12, abs_tol=1e-9)
True
2.39.3. Penyimpangan akumulasi¶
Penjumlahan panjang float kehilangan presisi lebih cepat pada MicroPython dibandingkan CPython, karena setiap hasil antara dibulatkan kembali ke presisi 32-bit:
total = 0.0
for _ in range(1000000):
total += 0.1
print(total) # noticeably off from 100000.0
Untuk penambahan berulang di mana akurasi penting, dua pola membantu:
Akumulasi ke dalam integer kapan pun nilainya dapat diskalakan ke integer -- bekerja dalam milidetik daripada detik, atau milivolt daripada volt, lalu konversikan sekali di akhir.
Hitung dalam batch lebih kecil dan jumlahkan hasil batch, sehingga setiap penambahan berada di antara nilai-nilai dengan besaran yang serupa.
Sisi integer tidak memiliki batasan seperti itu -- integer MicroPython memiliki presisi arbitrer, seperti CPython. Di mana Anda memiliki pilihan, utamakan aritmatika integer untuk hal-hal di mana kehilangan presisi akan bertambah.