Mengapa resolusi angka floating point menurun lebih jauh dari asal?


19

Adegan OpenGL saya memiliki objek yang diposisikan pada jarak yang sangat jauh dari asalnya. Ketika saya melihat objek-objek ini, dan menggeser / memutar / memperbesar kamera di sekitar mereka, mereka 'jitter'. Yaitu, simpul-simpul yang terdiri dari objek-objek itu seolah-olah membentur sebuah kotak 3d poin imajiner. Saya sudah membaca ini adalah masalah umum karena jumlah informasi yang dapat disimpan menggunakan presisi floating point (yang OpenGL, dan cukup banyak hal lain gunakan). Saya tidak mengerti mengapa ini terjadi.

Saat mencari solusi, saya menemukan perbaikan 'asal mengambang' yang sangat sederhana, dan sepertinya berhasil. Saya hanya mengubah segalanya sehingga objek saya berada pada posisi relatif yang sama tetapi apa pun yang dilihat kamera saya dekat dengan aslinya. Saya menemukan penjelasan di sini: http://floatingorigin.com/ , tetapi saya tidak bisa mengikutinya.

Jadi ... Bisakah seseorang menjelaskan mengapa memposisikan adegan saya sangat jauh (katakanlah 10 juta unit) dari hasil asal dalam perilaku tidak menentu yang saya amati? Dan juga mengapa pindah dekat dengan asal memperbaiki masalah?


4
Karena jika tidak, mereka akan menjadi angka-angka tetap . Pertanyaan tautologis, ini.
MSalters

1
Benar, tetapi hanya ketika Anda mengerti apa arti 'floating point' sebenarnya.
Kylotan

Jawaban:


26

Ini SEMUA karena cara floating point direpresentasikan dalam komputer.

Integer disimpan secara langsung; setiap unit persis "satu" terpisah dari "sebelumnya" seperti yang Anda harapkan dengan angka yang dapat dihitung.

Dengan angka floating point ini tidak persis demikian. Sebagai gantinya, beberapa bit mengindikasikan EXPONENT, dan sisanya menunjukkan apa yang dikenal sebagai mantissa , atau bagian fraksional yang kemudian DIBAGAI oleh bagian eksponen (secara implisit 2 ^ exp) untuk memberikan hasil akhir.

Lihat di sini untuk penjelasan visual bit.

Justru karena eksponen ini menjadi bagian aktual dari bit-bit itu presisi mulai WANE begitu angka tumbuh lebih besar.

Untuk melihat ini dalam tindakan, mari kita lakukan representasi titik mengambang-faux tanpa masuk ke seluk-beluk: ambil eksponen kecil seperti 2 dan lakukan beberapa bagian fraksional untuk menguji:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

... dll.

Angka-angka ini tidak tumbuh sangat jauh hanya pada eksponen 2. Tapi sekarang mari kita coba eksponen 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Wah, perbedaan besar sekarang!

Contohnya, walaupun tidak secara khusus pergi ke COUNTABLE SANGAT BERIKUTNYA (itu akan menjadi bagian fraksional berikutnya tergantung pada berapa banyak bit itu), ada di sana untuk menunjukkan kehilangan presisi setelah angka tumbuh lebih besar. Unit "selanjutnya yang dapat dihitung" dalam mengapung sangat kecil dengan eksponen kecil dan SANGAT besar dengan eksponen lebih besar, sedangkan dalam bilangan bulat SELALU 1.

Alasan metode asal float bekerja adalah karena itu scaling semua angka-angka floating-eksponen berpotensi besar ini BAWAH ke eksponen kecil sehingga "countables berikutnya" (presisi) bisa sangat kecil dan bahagia.


Contoh yang Anda berikan benar-benar ilustratif, terima kasih :)
Pris

3
Di jalur yang benar, tetapi saya berharap Anda menggunakan contoh yang lebih dekat dengan cara floating point bekerja. Itu tidak menaikkan mantissa menjadi eksponen; itu mantissa * 2 ^ eksponen.
Nathan Reed

3
Anda benar, saya tahu itu; Saya tidak tahu apa yang saya pikirkan. Mengedit jawaban saya.

1
@ScottW Suntingan yang bagus! +1
Nathan Reed

17

Karena angka floating point direpresentasikan sebagai fraksi + eksponen + tanda, dan Anda hanya memiliki jumlah bit yang tetap untuk bagian fraksi.

http://en.wikipedia.org/wiki/Single_precision

Ketika Anda mendapatkan angka yang lebih besar dan lebih besar, Anda tidak memiliki bit untuk mewakili bagian yang lebih kecil.


8

Klasik di lapangan harus diangkat: Apa yang harus diketahui setiap ilmuwan komputer tentang angka floating point .

Tetapi intinya berkaitan dengan bagaimana bilangan floating point presisi tunggal (ganda) hanya bilangan biner 32 bit (64-bit) dengan 1 bit mewakili tanda, eksponen 8-bit (11-bit) dari basis 2 , dan 23bit (52-bit) secara signifikan (tanda kurung adalah nilai untuk ganda).

Itu berarti angka positif terkecil yang dapat Anda wakili dalam presisi tunggal adalah 0,0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .

Angka positif berikutnya adalah dua kali lipat yaitu: 0,000000000000000000000010 x 2 -127 = 2 -148 ~ 2,80 x 10 -45 , dan kemudian angka berikutnya adalah jumlah dari dua sebelumnya 0,0000000000000000000000000000000000000000000000000000 x00 -127 = 3 x 2 -149 ~ 4.2 - 45 .

Ini terus meningkat dengan perbedaan konstan yang sama hingga: 0,111111111111111111111111 x 2 -127 = 2 -126 - 2 149 ~ 1,17549435 x 10 -38 - 0,00000014 x 10 -38 = 1,17549421 x 10 -38

Sekarang Anda telah mencapai angka normal (di mana angka pertama dalam signifikans adalah 1) khususnya: 1,000000000000000000000000 x 2 -126 = 2 -126 = 1,17549435 x 10 -38 dan angka berikutnya adalah 1,000000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 x 1.00000023.


2

Alasan mengapa angka titik-mengambang menjadi kurang tepat lebih jauh dari titik asal adalah karena angka titik-mengambang seharusnya mampu mewakili angka besar. Cara ini dilakukan meminjamkannya istilah "floating point". Ia membagi nilai-nilai yang mungkin dapat diambilnya (yang ditentukan oleh panjang bitnya) sehingga ada kira-kira angka yang sama untuk setiap eksponen: Untuk float 32-bit, 23 bit menentukan mantissa atau signifikansi. Jadi itu akan dapat mengambil nilai 2 ^ 23 nilai yang berbeda di setiap rentang eksponen. Salah satu rentang eksponen ini adalah 1-2 [2 ^ 0 hingga 2 ^ 1], jadi memisahkan rentang 1 ke 2 menjadi 2 ^ 23 nilai yang berbeda memungkinkan banyak presisi.

Tetapi memisahkan rentang [2 ^ 10 ke 2 ^ 11] menjadi 2 ^ 23 nilai yang berbeda berarti ruang antara masing-masing nilai jauh lebih besar. Jika tidak, maka 23 bit tidak akan cukup. Semuanya adalah kompromi: Anda membutuhkan jumlah bit yang tak terbatas untuk mewakili bilangan real. Jika aplikasi Anda bekerja dengan cara yang memungkinkan Anda lolos dengan presisi yang lebih rendah untuk nilai yang lebih besar, dan Anda mendapat manfaat karena dapat benar - benar mewakili nilai-nilai besar , Anda menggunakan representasi floating point.


hanya membuat catatan di sini setelah ditinjau lebih lanjut 7 tahun kemudian ... nomor saya dalam contoh saya tidak terlalu dipilih dengan baik. Tapi poin keseluruhannya valid.
Steven Lu

1

Mungkin agak sulit untuk menawarkan contoh spesifik tentang bagaimana presisi floating-point bekerja. Untuk melengkapi jawaban lain, inilah satu. Katakanlah kita memiliki angka floating-point desimal , dengan tiga digit mantissa dan satu digit eksponen:

mantissa × 10 eksponen

Ketika eksponen adalah 0, setiap bilangan bulat dalam kisaran 0–999 dapat direpresentasikan secara tepat. Saat 1, Anda pada dasarnya mengalikan setiap elemen dari rentang itu dengan 10, sehingga Anda mendapatkan rentang 0–9990; tetapi sekarang, hanya kelipatan 10 yang dapat direpresentasikan secara akurat, karena Anda masih memiliki presisi tiga digit. Ketika eksponen berada pada maksimum 9, perbedaan antara setiap pasangan bilangan bulat yang diwakili adalah satu miliar . Anda benar-benar memperdagangkan presisi untuk kisaran.

Ini bekerja dengan cara yang sama dengan angka-angka titik-mengambang biner: setiap kali eksponen naik satu, rentangnya berlipat ganda , tetapi jumlah nilai yang dapat diwakili dalam rentang itu dikurangi setengahnya . Ini berlaku untuk bilangan pecahan juga, yang tentu saja merupakan sumber masalah Anda.


0

Secara umum, resolusi semakin buruk karena resolusi dikalikan dengan nilai eksponen (2 ** bagian eksponen).

sebagai pengakuan atas komentar josh: yang di atas hanya untuk menempatkan jawabannya dalam pernyataan singkat. Tentu saja, seperti yang telah saya coba tunjukkan pada http://floatingorigin.com/ , ini baru akan dimulai menuju solusi keseluruhan dan program Anda dapat memiliki jitter dari sejumlah tempat: dalam pipa presisi atau bagian lain dari kode .


Ini tidak menambahkan apa pun yang belum ada dalam jawaban lain.

Benar: Saya menyadari bahwa saya dapat menggambarkan jawaban dalam satu baris dan berpikir seseorang mungkin menemukan jawaban singkat yang berguna.
Chris Thorne

-1

Buffer kedalaman OpenGL tidak linier . Semakin jauh Anda pergi, semakin buruk resolusi yang dimilikinya. Saya sarankan untuk membaca ini . Sesuatu diambil dari sana (12.070):

Singkatnya, pembagian perspektif, berdasarkan sifatnya, menyebabkan lebih presisi Z dekat dengan bagian depan volume tampilan daripada di belakang.

Dan satu lagi (12.040):

Anda mungkin telah mengkonfigurasi pesawat kliping zNear dan zFar Anda dengan cara yang sangat membatasi presisi penyangga kedalaman Anda. Secara umum, ini disebabkan oleh nilai bidang kliping zNear yang terlalu dekat dengan 0,0. Karena bidang kliping zNear semakin dekat dengan 0,0, presisi efektif dari buffer kedalaman berkurang secara dramatis. Memindahkan bidang kliping zFar lebih jauh dari mata selalu memiliki dampak negatif pada presisi penyangga kedalaman, tetapi itu tidak sedramatis memindahkan bidang kliping zNear.

Jadi, Anda harus memindahkan pesawat kliping terdekat Anda sejauh mungkin dan pesawat jauh Anda terdekat yang Anda bisa.


-1: pertanyaannya adalah tentang presisi floating-point, bukan masalah presisi dengan representasi kedalaman buffer non-linear.
Nathan Reed

Mungkin saja yang saya lihat adalah karena masalah buffering yang dalam. Saya menggunakan lib di atas OpenGL untuk melihat adegan saya, dan saya membuat asumsi pengaturan kamera, melihat, dan pesawat kliping dekat dan jauh untuk memperhitungkan ukuran dan posisi geometri (karena penonton alat tampaknya secara otomatis mengatur tampilan optimal untuk konten adegan). Tapi saya kira ini mungkin tidak terjadi - saya akan mencoba bermain-main dengan pesawat kliping meninggalkan posisi asli dan melihat apa yang terjadi
Pris

2Nathan Reed: Penulis menulis, bahwa dia memiliki adegan OpenGL, jadi saya pikir, bisa jadi ini masalah juga.
zacharmarz

Masalah ini mungkin tampak mirip atau terkait, tetapi nilai buffer kedalaman pasti TIDAK disimpan dengan cara yang kompatibel dengan angka floating point. Ini adalah format titik tetap. Karena inilah jawabannya mungkin menyesatkan.
Steven Lu
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.