Float Exposed
(float.exposed)- Artikel ini menjelaskan bagaimana nilai floating-point (float) disimpan di memori dan direpresentasikan
- Fokus pada bentuk heksadesimal dan desimal dari nilai serta cara mengubahnya ke nilai numerik sebenarnya
- Menjelaskan definisi area Sign, Exponent, Significand dan peran masing-masing
- Termasuk contoh cara menafsirkan nilai float tertentu merepresentasikan nilai biner dan desimal yang persis seperti apa
- Juga membahas perhitungan selisih (Delta) antara nilai-nilai yang dapat direpresentasikan
Analisis struktur penyimpanan nilai floating-point
- Terdapat berbagai format floating-point seperti "halfb float float double"
- Setiap nilai dapat diperiksa sebagai nilai yang disimpan di memori, seperti Raw Hexadecimal Integer Value (nilai integer heksadesimal mentah) dan Raw Decimal Integer Value (nilai integer desimal mentah)
- Data heksadesimal dihubungkan ke notasi floating-point aktual melalui Hexadecimal Form ("%a")
- Posisi masing-masing nilai ditunjukkan sebagai Significand–Exponent Range (posisi dalam rentang significand–exponent)
Cara menafsirkan nilai biner dan desimal
- Bilangan floating-point dapat dinyatakan dalam Base-2 (ekspresi evaluasi basis 2) sebagai berikut:
- (−12)02×102(100010012 − 011111112)×1.011111110010100000000002
→ Ini adalah evaluasi nilai melalui ekspresi biner
- (−12)02×102(100010012 − 011111112)×1.011111110010100000000002
- Dalam Base-10 (ekspresi evaluasi basis 10), bentuknya seperti ini:
- 1×210×1.4967041015625
→ Dinyatakan sebagai hasil kali 2 pangkat 10 dan bagian pecahan
- 1×210×1.4967041015625
- Nilai desimal yang tepat saat dikonversi juga ditampilkan:
- Disajikan dalam bentuk seperti 1.532625×103
Menghitung jarak ke nilai tetangga (Delta)
- Delta (jarak) antara nilai-nilai yang dapat direpresentasikan memiliki makna penting
- Masing-masing memberikan jarak ke nilai berikutnya atau sebelumnya yang dapat direpresentasikan (Delta to Next/Previous Representable Value)
- Contoh: ±1.220703125×10-4
- Jarak ini berkaitan dengan digit signifikan/presisi dari nilai floating-point
Ringkasan
- Representasi di memori untuk floating-point serta prinsip konversi biner dan desimal
- Penjelasan struktur sign, exponent, significand
- Juga merangkum informasi rentang representasi dan jarak ke nilai tetangga
1 komentar
Pendapat Hacker News
Untuk topik ini, penjelasan ini yang paling bagus: https://fabiensanglard.net/floating_point_visually_explained/ Saya menemukan tulisan ini saat mulai membaca Hacker News, dan itu memberi motivasi bahwa konten seperti ini layak terus ada di platform: https://news.ycombinator.com/item?id=29368529
Mungkin saya terlalu condong ke sisi matematis, tetapi penjelasan itu tidak terasa semudah itu Jika menginginkan penjelasan yang benar-benar sederhana tentang floating point: ia memberi presisi dengan jumlah bit yang kira-kira sama terlepas dari skalanya Artinya, entah angkanya jauh lebih kecil dari 1, di sekitar 1, atau sangat besar, Anda bisa mengharapkan tingkat akurasi yang hampir sama pada bit-bit terdepan Itulah sifat intinya, tetapi tidak mudah untuk benar-benar menginternalisasikannya
Ini cocok sekali dengan konteks blog yang baru-baru ini ditulis tim riset TM https://news.ycombinator.com/item?id=45200925
Saya belum pernah melihat ini dijelaskan sebaik ini, jadi terima kasih sudah membagikannya
Salah satu masalah yang lama saya pikirkan adalah "bagaimana merepresentasikan nilai float sebagai string desimal yang paling pendek namun tetap jelas" Misalnya, saat memakai float presisi tunggal, dibutuhkan hingga 9 digit presisi desimal untuk mengidentifikasi float secara unik Jadi Anda perlu memakai pola printf seperti %.9g Tetapi dengan begitu, 0.1 akan tercetak sebagai nilai jelek seperti 0.100000001 Karena itu biasanya orang membulatkan dan menampilkannya dengan 6 digit, dan jika memakai %.6g, nilai desimal yang dimasukkan hingga 6 digit bisa dicetak sama dengan nilai yang tersimpan Namun untuk nilai hasil perhitungan, round-trip tidak lagi aman Ini penting khususnya ketika nilai float harus dibandingkan secara tepat, misalnya untuk memeriksa apakah data berubah Ide yang saya pikirkan adalah pertama cetak dengan 6 digit, lalu jika setelah diparse kembali menghasilkan nilai biner yang sama, pakai itu; jika tidak, coba lagi dengan 7 digit, 8 digit, sampai 9 digit untuk menemukan representasi desimal terpendek Algoritma saya seperti di bawah ini
Saya penasaran apakah ada cara yang lebih efisien untuk menemukan representasi terpendek tanpa mengulang printf/scanf
Masalah ini memang penting dalam praktik Ini bisa dipandang sebagai masalah membuat string "ternormalisasi" untuk float tertentu Karena itu ada berbagai algoritma efisien seperti Dragon4, Grisu3, Ryu, dan Dragonbox Library double-conversion dari Google juga mengimplementasikan dua yang pertama
Ada cara yang lebih baik untuk mendapatkannya tanpa loop printf/scanf Bahkan
printf("%f", ...)saja bisa melakukannya Algoritma sebenarnya untuk mengubah float menjadi string cukup rumit Algoritma bagus yang relatif baru adalah https://github.com/ulfjack/ryu Saya rasa belakangan ada metode yang bahkan lebih efisien, tetapi saya tidak ingat namanyaTidak perlu terlalu memikirkan komentar negatif; meski mungkin bukan cara terbaik, selama tidak ada bug, biasanya itu bekerja cukup baik Saya juga pernah punya pengalaman mirip: suatu kali saya ingin mencari vektor yang akan menjadi vektor yang sama setelah rotasi Euler (5°, 5°, 0), jadi saya sedikit menggeser vektor secara acak dan melihat apakah ia makin dekat ke vektor acuan Saya menjalankan loop jutaan kali dan mendapat hasil dalam beberapa detik di Python Untuk level library itu mungkin tidak efisien, tetapi untuk kebutuhan saya sendiri saya sangat puas
Lihat
std::numeric_limits<float>::max_digits10https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10.htmlTidak ada gunanya, dan Anda sama sekali tidak boleh memakai sscanf() Ubah ke integer tak bertanda lalu serialisasikan/pulihkan; itu reversibel tanpa kehilangan informasi
Jika butuh representasi yang lebih pendek, gunakan heuristik yang memungkinkan pemulihan semula, asalkan metode itu menjamin presisi asli (misalnya idempotensi)
Tip favorit saya soal FP adalah bahwa perbandingan float hampir bisa dipakai seperti perbandingan integer Untuk menentukan a > b, Anda bisa menafsirkan a dan b sebagai integer bertanda lalu membandingkannya begitu saja Cara ini bekerja (hampir) dengan baik Artinya, nilai float berikutnya yang lebih besar adalah pola bit yang sama setelah dikonversi ke integer lalu ditambah 1 Misalnya, mulai dari float 0.0 lalu tambahkan 1 dengan penjumlahan integer; itulah nilai float berikutnya (denormal, celah terkecil)
nextafterjuga diimplementasikan dengan prinsip ini Mengetahui bahwa nilai float mengikuti urutan perbandingan integer membuatnya terasa jauh lebih alami Tentu ada pengecualian: NaN, tak hingga, dan negatif 0 berbeda Ada beberapa kegunaan praktis, walau tidak untuk semuanyaPernyataan itu tidak tepat sepenuhnya Itu benar untuk bilangan positif atau perbandingan positif-negatif, tetapi berbeda untuk perbandingan antar bilangan negatif Floating point standar (
float) memakai sign-magnitude, sedangkan integer bertanda modern memakai komplemen dua Pada bilangan negatif, arah perbandingan besarannya terbalik di antara keduanya Jika Anda menaikkan float seperti int satu per satu, biasanya Anda bergerak ke nilai dengan "magnitudo" lebih besar dalam tanda yang sama Artinya, bilangan positif naik, sedangkan bilangan negatif turun ke negatif yang lebih kecil lagi Pada integer, nilainya selalu naik, atau terkena overflow Cara yang lebih tepat untuk mengatakannya adalah bahwa ia sama seperti perbandingan integer sign-magnitude Tentu saja caveat yang sudah disebutkan tetap berlakuSebagai referensi, algoritma perbandingan besar floating point total-order di library standar Rust yang juga dapat membandingkan NaN adalah sebagai berikut (rekomendasi IEEE 751)
Lihat algoritma lengkapnya
Saya menemukan hal ini dalam mata kuliah game AI OMSCS saya, dalam konteks hal-hal yang perlu diperhatikan saat merepresentasikan posisi objek game dengan floating point Semakin jauh dari titik asal atau titik referensi, float harus menyimpan nilai yang lebih besar sehingga presisinya berkurang, dan itu berbahaya
Menarik bahwa fenomena ini menjadi semacam mitos Minecraft sebagai Far Lands Artinya, semakin jauh dari origin dunia, generasi terrain atau fisika sedikit demi sedikit menjadi aneh, lalu jauh lebih jauh lagi semuanya benar-benar rusak Ada nuansa agak okultis, seperti hukum realitas yang perlahan runtuh Dan semua itu terjadi karena batas presisi float
Saat menjumlahkan banyak angka float antara 0 dan 1, membandingkan penjumlahan berurutan biasa dengan cara menjumlahkan per pasangan lalu menggabungkannya kembali, metode berpasangan jauh lebih akurat Ini contoh betapa seriusnya dampak galat akumulasi float Memang pernah ada kasus di mana galat float seperti ini diabaikan lalu menimbulkan masalah Dalam "The Art of Computer Programming" karya Donald Knuth, kebenaran dasar tentang float seperti a + (b + c) ≠ (a + b) + c dijelaskan dengan baik Di dunia nyata pun pernah ada masalah akibat ini: sistem misil Patriot menangani akumulasi waktu dengan float, lalu galatnya makin lama makin besar hingga sepenuhnya meleset dari target dan perlu di-restart Sistem itu perlu di-boot ulang setiap 24 jam, dan akhirnya software sistemnya diperbaiki Bahkan pernah ada struktur besar runtuh karena galat float (nilai ketebalan dihitung terlalu tipis)
Anda perlu mendefinisikan kondisi batas terlebih dahulu agar ada patokan berapa presisi yang dibutuhkan Dengan begitu, jarak minimum dan maksimum juga bisa dihitung sebelumnya Jika dunia terlalu besar, Anda perlu membaginya menjadi sektor atau mengelola koordinat global/lokal secara terpisah (misalnya No Man's Sky) Game pada dasarnya hanyalah perangkat teater Dengan Double-Precision, kebanyakan situasi biasanya sudah cukup aman Yang penting adalah ingat untuk tidak menjumlahkan nilai kecil dan besar secara langsung
Kerbal Space Program memakai rekayasa yang sangat cerdas untuk mencoba mewujudkan tata surya hanya dengan float 32 bit Ada banyak artikel dan video terkait, sangat saya rekomendasikan
Visualisasi ini menyenangkan, dan menarik karena mirip secara visual dengan CIDR range calculator yang dulu saya buat untuk membantu memahami rentang jaringan Visualisasi seperti ini sangat berguna
Dulu saya memakai https://www.h-schmidt.net/FloatConverter/IEEE754.html untuk mengeksplorasi representasi float Kelebihan situs ini adalah ia juga menampilkan galat konversi, tetapi tidak mendukung double precision
Belum dibagikan di komentar ini, tetapi situs favorit saya soal float adalah https://0.30000000000000004.com/
Untuk float 32 bit, "integer paling menarik" kemungkinan adalah 16777217 (untuk 64 bit, 9007199254740992) Mengetahui edge case seperti ini saat pengujian cukup menyenangkan
Untuk float 64 bit, 9007199254740991 adalah
Number.MAX_SAFE_INTEGERdi JavaScript Nilai ini bukan bilangan genap, dan nilai berikutnya 9007199254740992 juga masih aman dengan sendirinya, tetapi nilai yang jelas tidak aman seperti 9007199254740993 akan dibulatkan sehingga tidak bisa dibedakanUntuk float 64 bit, yang tepat adalah ±9,007,199,254,740,993.0 :-) Sebagai catatan, nilai-nilai ini berarti nilai tepat setelah batas integer terbesar yang masih bisa direpresentasikan float secara "presisi" Misalnya pada float 32 bit, setelah ±16,777,216.0, nilai berikutnya yang bisa direpresentasikan adalah ±16,777,218.0 ±16,777,217.0 tidak bisa direpresentasikan dan biasanya dibulatkan, misalnya ke arah nol Batas presisi dan masalah pembulatan seperti ini sering terlewatkan
Saya senang IEEE754 itu ada, tetapi menurut saya IEEE754 tidak sempurna dan nilai seperti posit lebih baik (jika tidak mengasumsikan ada dukungan hardware) BigNum rational (bilangan rasional) bahkan lebih unggul daripada keduanya, tetapi paling lambat
Akan sangat keren jika ini juga mendukung berbagai format fp8 yang belakangan diperkenalkan di GPU