Pada penanganan angka floating point dengan cara deterministik
Floating point bersifat deterministik. Yah, seharusnya begitu. Itu rumit.
Ada banyak literatur tentang angka floating point:
Dan bagaimana mereka bermasalah:
Untuk abstrak. Setidaknya, pada utas tunggal, operasi yang sama, dengan data yang sama, yang terjadi dalam urutan yang sama, harus bersifat deterministik. Jadi, kita bisa mulai dengan mengkhawatirkan input, dan menyusun ulang.
Salah satu input yang menyebabkan masalah adalah waktu.
Pertama-tama, Anda harus selalu menghitung timestep yang sama. Saya tidak mengatakan untuk tidak mengukur waktu, saya mengatakan bahwa Anda tidak akan melewatkan waktu untuk simulasi fisika, karena variasi waktu adalah sumber kebisingan dalam simulasi.
Mengapa Anda mengukur waktu jika Anda tidak meneruskannya ke simulasi fisika? Anda ingin mengukur waktu yang telah berlalu untuk mengetahui kapan langkah simulasi harus dipanggil, dan - dengan asumsi Anda menggunakan tidur - berapa banyak waktu untuk tidur.
Jadi:
- Ukur waktu: Ya
- Gunakan waktu dalam simulasi: Tidak
Sekarang, instruksi pemesanan ulang.
Kompilator dapat memutuskan bahwa f * a + b
itu sama dengan b + f * a
, namun itu mungkin memiliki hasil yang berbeda. Itu juga dapat dikompilasi ke fmadd , atau dapat memutuskan mengambil beberapa baris seperti itu yang terjadi bersama dan menulisnya dengan SIMD , atau beberapa pengoptimalan lain yang tidak dapat saya pikirkan saat ini. Dan ingat kami ingin operasi yang sama terjadi pada urutan yang sama, itu menjadi alasan bahwa kami ingin mengendalikan operasi apa yang terjadi.
Dan tidak, menggunakan dobel tidak akan menyelamatkan Anda.
Anda perlu khawatir tentang kompiler dan konfigurasinya, khususnya untuk menyinkronkan angka floating point di seluruh jaringan. Anda perlu mendapatkan build untuk menyetujui untuk melakukan hal yang sama.
Bisa dibilang, menulis majelis akan ideal. Dengan begitu Anda memutuskan operasi apa yang harus dilakukan. Namun, itu bisa menjadi masalah untuk mendukung banyak platform.
Jadi:
Kasus untuk nomor titik tetap
Karena cara mengapung diwakili dalam memori, nilai-nilai besar akan kehilangan presisi. Ini menjadi alasan bahwa menjaga nilai-nilai Anda kecil (penjepit) mengurangi masalah. Dengan demikian, tidak ada kecepatan besar dan tidak ada kamar besar. Yang juga berarti Anda dapat menggunakan fisika diskrit karena risiko risikonya lebih kecil.
Di sisi lain, kesalahan kecil akan menumpuk. Jadi, terpotong. Maksud saya, skala dan dilemparkan ke tipe integer. Dengan begitu Anda tahu tidak ada yang menumpuk. Akan ada operasi yang dapat Anda lakukan dengan tetap menggunakan tipe integer. Ketika Anda perlu kembali ke floating point, Anda membuang dan membatalkan penskalaan.
Catatan saya katakan skala. Idenya adalah bahwa 1 unit sebenarnya akan direpresentasikan sebagai kekuatan dua (16384 misalnya). Apa pun itu, buatlah itu konstan dan gunakanlah. Anda pada dasarnya menggunakannya sebagai nomor titik tetap. Bahkan, jika Anda dapat menggunakan nomor titik tetap yang tepat dari beberapa pustaka yang dapat diandalkan, jauh lebih baik.
Saya katakan truncate. Tentang masalah pembulatan, itu berarti Anda tidak bisa mempercayai sedikit pun dari nilai apa pun yang Anda dapatkan setelah pemeran. Jadi, sebelum skala pemeran mendapatkan sedikit lebih banyak dari yang Anda butuhkan, dan potong setelahnya.
Jadi:
- Pertahankan nilai kecil: Ya
- Pembulatan yang cermat: Ya
- Memperbaiki nomor titik bila memungkinkan: Ya
Tunggu, mengapa Anda membutuhkan floating point? Bisakah Anda tidak bekerja hanya dengan tipe integer? Oh benar Trigonometri dan radikalisasi. Anda dapat menghitung tabel untuk trigonometri dan radikasi dan membuatnya dipanggang di sumber Anda. Atau Anda dapat menerapkan algoritma yang digunakan untuk menghitungnya dengan angka titik mengambang, kecuali menggunakan nomor titik tetap. Ya, Anda perlu menyeimbangkan memori, kinerja, dan presisi. Namun, Anda bisa tetap keluar dari angka floating point, dan tetap deterministik.
Tahukah Anda bahwa mereka melakukan hal-hal seperti itu untuk PlayStation asli? Silakan Temui Anjing Saya, Tambalan .
By the way, saya tidak mengatakan untuk tidak menggunakan floating point untuk grafik. Hanya untuk fisika. Maksudku, tentu saja, posisi akan tergantung pada fisika. Namun, seperti yang Anda tahu collider tidak harus cocok dengan model. Kami tidak ingin melihat hasil pemotongan model.
Jadi: GUNAKAN ANGKA TETAP TETAP.
Agar jelas, jika Anda dapat menggunakan kompiler yang memungkinkan Anda menentukan cara floating point bekerja, dan itu sudah cukup untuk Anda, maka Anda bisa melakukannya. Itu tidak selalu menjadi pilihan. Selain itu, kami melakukan ini untuk determinisme. Nomor titik tetap tidak berarti tidak ada kesalahan, setelah semua mereka memiliki presisi terbatas.
Saya tidak berpikir bahwa "nomor titik tetap itu sulit" adalah alasan yang baik untuk tidak menggunakannya. Dan jika Anda ingin alasan yang baik untuk menggunakannya, itu adalah determinisme, khususnya determinisme lintas platform.
Lihat juga:
Tambahan : Saya menyarankan agar ukuran dunia kecil. Dengan mengatakan itu, Kedua OP ans Jibb Smart memunculkan titik bahwa bergerak menjauh dari pelampung asal memiliki presisi kurang. Itu akan memiliki efek pada fisika, yang akan terlihat jauh lebih awal daripada ujung dunia. Nomor titik tetap, baik, memiliki presisi tetap, mereka akan sama baik (atau buruk, jika Anda suka) di mana-mana. Mana yang baik jika kita menginginkan determinisme. Saya juga ingin menyebutkan bahwa cara kita biasanya melakukan fisika memiliki sifat memperkuat variasi kecil. Lihat Efek Kupu-Kupu - Fisika Deterministik dalam Mesin Luar Biasa dan Pembuat Alat .
Cara lain untuk melakukan fisika
Saya telah berpikir, alasan mengapa kesalahan kecil dalam presisi dalam angka floating point menguat adalah karena kita melakukan iterasi pada angka-angka itu. Setiap langkah simulasi, kami mengambil hasil dari langkah simulasi terakhir dan melakukan hal-hal pada mereka. Mengumpulkan kesalahan di atas kesalahan. Itu adalah efek kupu-kupu Anda.
Saya tidak berpikir kita akan melihat satu build menggunakan satu thread pada mesin yang sama menghasilkan output yang berbeda dengan input yang sama. Namun, pada mesin lain itu bisa, atau bangunan lain bisa.
Ada argumen untuk pengujian di sana. Jika kita memutuskan dengan tepat bagaimana hal-hal seharusnya bekerja, dan kita dapat menguji pada perangkat keras target, kita tidak boleh memadamkan build yang memiliki perilaku berbeda.
Namun, ada juga argumen untuk tidak bekerja jauh yang mengakumulasi begitu banyak kesalahan. Mungkin ini adalah kesempatan untuk melakukan fisika dengan cara yang berbeda.
Seperti yang mungkin Anda ketahui, ada fisika kontinu dan diskrit, keduanya bekerja pada seberapa banyak masing-masing objek akan maju pada catatan waktu. Namun, fisika kontinu memiliki cara untuk mencari tahu instan tabrakan, bukan menyelidiki berbagai kemungkinan yang mungkin untuk melihat apakah tabrakan terjadi.
Jadi, saya mengusulkan yang berikut: gunakan teknik fisika kontinu untuk mencari tahu kapan tabrakan berikutnya dari setiap objek akan terjadi, dengan timestep besar, jauh lebih besar dari yang ada pada satu langkah simulasi tunggal. Kemudian Anda mengambil instan tabrakan terdekat dan mencari tahu di mana semuanya akan berada pada saat itu.
Ya, itu banyak pekerjaan dari satu langkah simulasi. Itu berarti simulasi itu tidak akan mulai secara instan ...
... Namun, Anda dapat mensimulasikan beberapa langkah simulasi berikutnya tanpa memeriksa tabrakan setiap kali, karena Anda sudah tahu kapan tabrakan berikutnya akan terjadi (atau bahwa tidak ada tabrakan yang terjadi di timestep besar). Selain itu, kesalahan yang diakumulasikan dalam simulasi tersebut tidak relevan karena setelah simulasi mencapai timestep besar, kami hanya menempatkan posisi yang kami hitung sebelumnya.
Sekarang, kita dapat menggunakan anggaran waktu yang akan kita gunakan untuk memeriksa tabrakan setiap langkah simulasi untuk menghitung tabrakan berikutnya setelah yang kita temukan. Itu adalah kita dapat mensimulasikan ke depan dengan menggunakan timestep besar. Dengan asumsi dunia terbatas dalam ruang lingkup (ini tidak akan bekerja untuk permainan besar), harus ada antrian negara masa depan untuk simulasi, dan kemudian setiap frame Anda hanya interpolasi dari negara terakhir ke yang berikutnya.
Saya akan berdebat untuk interpolasi. Namun, mengingat ada percepatan, kita tidak bisa begitu saja menginterpolasi semuanya dengan cara yang sama. Sebagai gantinya kita perlu menginterpolasi dengan mempertimbangkan percepatan setiap objek. Dalam hal ini kita bisa memperbarui posisi dengan cara yang sama seperti yang kita lakukan untuk timestep besar (yang juga berarti lebih sedikit kesalahan karena kita tidak akan menggunakan dua implementasi berbeda untuk gerakan yang sama).
Catatan : Jika kita melakukan angka floating point ini, pendekatan ini tidak memecahkan masalah objek yang berperilaku berbeda semakin jauh dari asal mereka. Namun, meskipun benar bahwa ketepatan hilang semakin jauh Anda pergi dari asal, itu masih deterministik. Bahkan, itu sebabnya bahkan tidak memunculkannya pada awalnya.
Tambahan
Dari OP dalam komentar :
Idenya adalah bahwa pemain akan dapat menyimpan mesin mereka dalam beberapa format (seperti xml atau json), sehingga posisi masing-masing dan rotasi dicatat. File xml atau json itu kemudian akan digunakan untuk mereproduksi mesin di komputer pemain lain.
Jadi, tidak ada format biner, kan? Itu berarti kita juga perlu khawatir apa pun angka floating point yang ditemukan sesuai dengan aslinya. Lihat: Float Precision Revisited: Portabilitas Float Sembilan Digit