Apakah matematika titik-mengambang konsisten dalam C #? Bisakah?


155

Tidak, ini bukan pertanyaan "Kenapa (1 / 3.0) * 3! = 1" .

Saya telah membaca tentang floating-point akhir-akhir ini; khususnya, bagaimana perhitungan yang sama dapat memberikan hasil yang berbeda pada arsitektur yang berbeda atau pengaturan optimasi.

Ini adalah masalah untuk gim video yang menyimpan replay, atau jaringan peer-to-peer (bukan server-klien), yang mengandalkan semua klien yang menghasilkan hasil yang persis sama setiap kali mereka menjalankan program - perbedaan kecil dalam satu perhitungan floating-point dapat menyebabkan kondisi permainan yang sangat berbeda pada mesin yang berbeda (atau bahkan pada mesin yang sama! )

Ini terjadi bahkan di antara prosesor yang "mengikuti" IEEE-754 , terutama karena beberapa prosesor (yaitu x86) menggunakan presisi ganda yang diperluas . Artinya, mereka menggunakan register 80-bit untuk melakukan semua perhitungan, kemudian memotong ke 64- atau 32-bit, yang mengarah ke hasil pembulatan yang berbeda dari mesin yang menggunakan 64- atau 32-bit untuk perhitungan.

Saya telah melihat beberapa solusi untuk masalah ini secara online, tetapi semua untuk C ++, bukan C #:

  • Nonaktifkan mode presisi diperluas ganda (sehingga semua doubleperhitungan menggunakan IEEE-754 64-bit) menggunakan _controlfp_s(Windows), _FPU_SETCW(Linux?), Atau fpsetprec(BSD).
  • Selalu jalankan kompiler yang sama dengan pengaturan optimisasi yang sama, dan minta semua pengguna memiliki arsitektur CPU yang sama (tidak ada permainan lintas platform). Karena "kompiler" saya sebenarnya adalah JIT, yang dapat mengoptimalkan secara berbeda setiap kali program dijalankan , saya rasa ini tidak mungkin.
  • Gunakan aritmatika titik tetap, dan hindari floatdan doublesama sekali. decimalakan bekerja untuk tujuan ini, tetapi akan jauh lebih lambat, dan tidak ada System.Mathfungsi perpustakaan yang mendukungnya.

Jadi, apakah ini bahkan masalah di C #? Bagaimana jika saya hanya bermaksud mendukung Windows (bukan Mono)?

Jika ya, apakah ada cara untuk memaksa program saya berjalan pada presisi ganda normal?

Jika tidak, apakah ada perpustakaan yang akan membantu menjaga penghitungan floating-point konsisten?


Saya telah melihat pertanyaan ini , tetapi setiap jawaban mengulangi masalah tanpa solusi, atau mengatakan "abaikan saja," yang bukan merupakan pilihan. Saya mengajukan pertanyaan serupa pada gamedev , tetapi (karena audiensi) sebagian besar jawaban tampaknya diarahkan ke C ++.
BlueRaja - Danny Pflughoeft

1
bukan jawaban, tapi saya yakin Anda di sebagian besar domain Anda dapat merancang sistem Anda sedemikian rupa sehingga semua negara yang dibagi menjadi deterministik, dan karena itu tidak ada penurunan kinerja yang signifikan
driushkin

1
@ Peter apakah Anda tahu emulasi floating point cepat untuk .net?
CodesInChaos

1
Apakah Java menderita masalah ini?
Josh

3
@Josh: Java memiliki strictfpkata kunci, yang memaksa semua perhitungan dilakukan dalam ukuran yang ditentukan ( floatatau double) daripada ukuran yang diperluas. Namun, Java masih memiliki banyak masalah dengan dukungan IEE-754. Sangat (sangat, sangat) sedikit bahasa pemrograman mendukung IEE-754 dengan baik.
porges

Jawaban:


52

Saya tahu tidak ada cara untuk membuat floating point yang deterministik normal dalam .net. JITter diizinkan untuk membuat kode yang berperilaku berbeda pada platform yang berbeda (atau antara versi .net yang berbeda). Jadi menggunakan normal floats dalam kode .net deterministik tidak mungkin.

Solusi yang saya pertimbangkan:

  1. Terapkan FixedPoint32 di C #. Meskipun ini tidak terlalu sulit (saya memiliki implementasi setengah jadi) rentang nilai yang sangat kecil membuatnya mengganggu untuk digunakan. Anda harus berhati-hati setiap saat sehingga Anda tidak meluap, atau kehilangan terlalu banyak presisi. Pada akhirnya saya menemukan ini tidak lebih mudah daripada menggunakan bilangan bulat secara langsung.
  2. Terapkan FixedPoint64 di C #. Saya menemukan ini agak sulit dilakukan. Untuk beberapa operasi, bilangan bulat menengah 128bit akan bermanfaat. Tapi .net tidak menawarkan tipe seperti itu.
  3. Terapkan floatingpoint 32 bit khusus. Kurangnya intrinsik BitScanReverse menyebabkan beberapa gangguan ketika menerapkan ini. Tetapi saat ini saya pikir ini adalah jalan yang paling menjanjikan.
  4. Gunakan kode asli untuk operasi matematika. Menghasilkan overhead panggilan delegasi pada setiap operasi matematika.

Saya baru saja memulai implementasi perangkat lunak matematika 32 bit floating point. Ini dapat melakukan sekitar 70 juta penambahan / perkalian per detik pada i3 2.66GHz saya. https://github.com/CodesInChaos/SoftFloat . Jelas itu masih sangat tidak lengkap dan bermasalah.


2
ada bilangan bulat berukuran "tidak terbatas" tersedia BigInteger meskipun tidak secepat int asli atau lama itu ada. NET memang menawarkan tipe seperti itu (dibuat untuk F # Saya percaya tetapi dapat digunakan dalam C #)
Rune FS

Pilihan lain adalah pembungkus GNU MP untuk .NET . Ini adalah pembungkus di sekitar GNU Multiple Precision Library yang mendukung bilangan bulat presisi, tak berhingga, rasional (fraksi), dan angka floating point.
Cole Johnson

2
Jika Anda akan melakukan semua ini, Anda sebaiknya mencoba decimaldulu, karena jauh lebih mudah untuk dilakukan. Hanya jika terlalu lambat untuk tugas yang dihadapi, pendekatan lain akan layak untuk dipikirkan.
Roman Starkov

Saya telah belajar tentang satu kasus khusus di mana floating point bersifat deterministik. Penjelasan yang saya dapatkan adalah: Untuk perkalian / pembagian, jika salah satu angka FP adalah kekuatan dua angka (2 ^ x), signifikan / mantissa tidak akan berubah selama perhitungan. Hanya eksponen yang akan berubah (titik akan bergerak). Jadi pembulatan tidak akan pernah terjadi. Hasilnya akan menjadi deterministik.
zigzag

Contoh: Angka seperti 2 ^ 32 direpresentasikan sebagai (eksponen: 32, mantissa: 1). Jika kita mengalikan ini dengan float lain (exp, man), hasilnya adalah (exp + 32, man * 1). Untuk divisi, hasilnya adalah (expo - 32, man * 1). Mengalikan mantissa dengan 1 tidak mengubah mantissa, jadi tidak masalah berapa banyak bit yang dimilikinya.
zigzag

28

Spesifikasi C # (§4.1.6 Jenis titik apung) secara khusus memungkinkan perhitungan titik apung dilakukan dengan menggunakan presisi yang lebih tinggi daripada hasil. Jadi, tidak, saya tidak berpikir Anda bisa membuat perhitungan itu secara langsung di .Net. Yang lain menyarankan berbagai solusi, sehingga Anda bisa mencobanya.


9
Saya baru menyadari bahwa spesifikasi C # tidak terlalu penting jika seseorang mendistribusikan rakitan yang dikompilasi. Itu hanya penting jika seseorang menginginkan kompatibilitas sumber. Yang benar-benar penting adalah spesifikasi CLR. Tapi saya cukup yakin jaminannya sama lemahnya dengan jaminan C #.
CodesInChaos

Tidakkah melakukan casting doublesetiap kali setelah operasi menghilangkan bit yang tidak diinginkan, menghasilkan hasil yang konsisten?
IllidanS4 ingin Monica kembali

2
@ IllidanS4 Saya tidak berpikir itu akan menjamin hasil yang konsisten.
svick

14

Halaman berikut mungkin berguna jika Anda membutuhkan portabilitas absolut dari operasi tersebut. Ini membahas perangkat lunak untuk menguji implementasi standar IEEE 754, termasuk perangkat lunak untuk meniru operasi floating point. Namun, sebagian besar informasi mungkin khusus untuk C atau C ++.

http://www.math.utah.edu/~beebe/software/ieee/

Catatan tentang titik tetap

Angka titik tetap biner juga dapat berfungsi dengan baik sebagai pengganti titik mengambang, seperti yang dibuktikan dari empat operasi aritmatika dasar:

  • Penambahan dan pengurangan sepele. Mereka bekerja dengan cara yang sama seperti bilangan bulat. Cukup tambahkan atau kurangi!
  • Untuk mengalikan dua angka titik tetap, kalikan dua angka kemudian geser ke kanan jumlah bit pecahan yang ditentukan.
  • Untuk membagi dua angka titik tetap, pindahkan dividen ke kiri jumlah bit fraksinya, kemudian bagi dengan pembagi.
  • Bab empat dari makalah ini memiliki panduan tambahan tentang penerapan angka titik tetap biner.

Nomor titik tetap biner dapat diimplementasikan pada semua tipe data integer seperti int, long, dan BigInteger, dan tipe yang tidak sesuai dengan CLS uint dan ulong.

Seperti yang disarankan dalam jawaban lain, Anda bisa menggunakan tabel pencarian, di mana setiap elemen dalam tabel adalah angka titik tetap biner, untuk membantu menerapkan fungsi kompleks seperti sinus, cosinus, akar kuadrat, dan sebagainya. Jika tabel pencarian kurang granular daripada nomor titik tetap, disarankan untuk membulatkan input dengan menambahkan setengah dari granularitas tabel pencarian ke input:

// Assume each number has a 12 bit fractional part. (1/4096)
// Each entry in the lookup table corresponds to a fixed point number
//  with an 8-bit fractional part (1/256)
input+=(1<<3); // Add 2^3 for rounding purposes
input>>=4; // Shift right by 4 (to get 8-bit fractional part)
// --- clamp or restrict input here --
// Look up value.
return lookupTable[input];

5
Anda harus mengunggah ini ke situs proyek kode sumber terbuka, seperti sourceforge atau github. Ini membuatnya lebih mudah untuk ditemukan, lebih mudah untuk berkontribusi, lebih mudah untuk menempatkan di resume Anda dll. Juga, beberapa tips kode sumber (jangan ragu untuk mengabaikan): Gunakan constsebagai ganti statickonstanta, sehingga kompiler dapat mengoptimalkannya; lebih suka fungsi anggota daripada fungsi statis (agar kita dapat memanggil, mis. myDouble.LeadingZeros()alih-alih IntDouble.LeadingZeros(myDouble)); cobalah untuk menghindari nama variabel satu huruf ( MultiplyAnyLength, misalnya, memiliki 9, membuatnya sangat sulit untuk diikuti)
BlueRaja - Danny Pflughoeft

Berhati-hatilah dalam menggunakan uncheckeddan tipe yang tidak sesuai CLS seperti ulong,, uintdll. Untuk tujuan kecepatan - karena mereka sangat jarang digunakan, JIT tidak mengoptimalkannya secara agresif, jadi menggunakannya sebenarnya bisa lebih lambat daripada menggunakan tipe normal seperti longdan int. Juga, C # memiliki kelebihan operator , yang mana proyek ini akan mendapat manfaat besar. Akhirnya, apakah ada tes unit terkait? Selain hal-hal kecil, pekerjaan luar biasa Peter, ini sangat mengesankan!
BlueRaja - Danny Pflughoeft

Terima kasih atas komentarnya. Saya melakukan tes unit pada kode. Mereka agak luas, meskipun, terlalu luas untuk dirilis untuk saat ini. Saya bahkan menulis rutinitas pembantu pengujian unit untuk membuat penulisan beberapa tes lebih mudah. Saya tidak menggunakan operator kelebihan beban untuk saat ini karena saya punya rencana menerjemahkan kode ke Jawa ketika saya selesai.
Peter O.

2
Yang lucu adalah ketika saya memposting di blog Anda, saya tidak melihat bahwa blog itu milik Anda. Saya baru saja memutuskan untuk mencoba google + dan di C # percikannya menyarankan entri blog itu. Jadi saya berpikir, "Sungguh kebetulan yang luar biasa bagi kami berdua untuk mulai menulis hal seperti itu pada saat yang sama". Tapi tentu saja kami punya pemicu yang sama :)
CodesInChaos

1
Mengapa repot-repot porting ini ke Jawa? Java sudah memastikan matematika floating point deterministik via strictfp.
Antimony

9

Apakah ini masalah untuk C #?

Iya. Arsitektur yang berbeda adalah yang paling tidak Anda khawatirkan, framerate yang berbeda, dll. Dapat menyebabkan penyimpangan karena ketidakakuratan dalam representasi float - bahkan jika itu adalah ketidakakuratan yang sama (misalnya arsitektur yang sama, kecuali GPU yang lebih lambat pada satu mesin).

Bisakah saya menggunakan System.Decimal?

Tidak ada alasan Anda tidak bisa, namun ini anjing lambat.

Apakah ada cara untuk memaksa program saya berjalan dengan presisi ganda?

Iya. Tuan rumah CLR runtime sendiri ; dan kompilasi dalam semua panggilan / bendera nessecary (yang mengubah perilaku aritmatika floating point) ke dalam aplikasi C ++ sebelum memanggil CorBindToRuntimeEx.

Apakah ada perpustakaan yang akan membantu menjaga penghitungan floating point konsisten?

Tidak yang saya tahu.

Apakah ada cara lain untuk menyelesaikan ini?

Saya telah mengatasi masalah ini sebelumnya, idenya adalah menggunakan QNumbers . Mereka adalah bentuk real yang merupakan titik tetap; tetapi bukan titik tetap pada basis-10 (desimal) - melainkan basis-2 (biner); karena ini primitif matematika pada mereka (tambah, sub, mul, div) jauh lebih cepat daripada basis-10 poin tetap naif; terutama jikan sama untuk kedua nilai (yang dalam kasus Anda akan menjadi). Selain itu karena mereka tidak terpisahkan mereka memiliki hasil yang jelas pada setiap platform.

Ingatlah bahwa framerate masih dapat memengaruhi ini, tetapi tidak seburuk dan mudah diperbaiki menggunakan titik sinkronisasi.

Bisakah saya menggunakan lebih banyak fungsi matematika dengan QNumbers?

Ya, pulang-pergi desimal untuk melakukan ini. Selain itu, Anda harus benar-benar menggunakan tabel pencarian untuk fungsi trig (sin, cos); karena mereka benar - benar dapat memberikan hasil yang berbeda pada platform yang berbeda - dan jika Anda mengkodekannya dengan benar, mereka dapat menggunakan QNumber secara langsung.


3
Tidak yakin apa yang Anda bicarakan dengan framerates menjadi masalah. Jelas Anda ingin memiliki tingkat pembaruan tetap (lihat contoh di sini ) - apakah itu sama dengan framerate tampilan tidak relevan. Selama ketidakakuratan sama pada semua mesin, kami baik-baik saja. Saya tidak mengerti jawaban ketiga Anda sama sekali.
BlueRaja - Danny Pflughoeft

@BlueRaja: Jawabannya "Apakah ada cara untuk memaksa program saya berjalan dengan presisi ganda?" baik untuk mengimplementasikan kembali seluruh Common Language Runtime, yang akan sangat rumit, atau menggunakan panggilan asli ke C ++ DLL dari aplikasi C #, seperti yang ditunjukkan dalam jawaban pengguna shelleybutterfly. Pikirkan "QNumbers" hanya sebagai angka titik tetap biner, seperti yang diisyaratkan dalam jawaban saya (saya belum sampai sekarang melihat angka titik tetap biner disebut "QNumbers".)
Peter O.

@Pieter O. Anda tidak perlu mengimplementasikan runtime. Server tempat saya bekerja di perusahaan saya menyelenggarakan runtime CLR sebagai aplikasi C ++ asli (demikian juga SQL Server). Saya sarankan Anda google CorBindToRuntimeEx.
Jonathan Dickinson

@BlueRaja tergantung pada game yang dimaksud. Menerapkan langkah-langkah framerate tetap untuk semua game bukanlah pilihan yang layak - karena algoritma AOE memperkenalkan latensi buatan; yang tidak dapat diterima misalnya dalam FPS.
Jonathan Dickinson

1
@ Jonathan: Ini hanya masalah di permainan peer-to-peer yang hanya mengirim input - untuk ini, Anda harus memiliki tingkat pembaruan tetap. Sebagian besar FPS tidak berfungsi seperti ini, tetapi beberapa yang tentu memiliki tingkat pembaruan tetap. Lihat pertanyaan ini .
BlueRaja - Danny Pflughoeft

6

Menurut entri blog MSDN yang agak lama ini , JIT tidak akan menggunakan SSE / SSE2 untuk floating point, semuanya x87. Karena itu, seperti yang Anda sebutkan, Anda harus khawatir tentang mode dan bendera, dan di C # itu tidak mungkin untuk dikendalikan. Jadi menggunakan operasi floating point normal tidak akan menjamin hasil yang sama persis pada setiap mesin untuk program Anda.

Untuk mendapatkan reproduksibilitas presisi ganda yang tepat, Anda harus melakukan emulasi floating point (atau titik tetap) perangkat lunak. Saya tidak tahu perpustakaan C # untuk melakukan ini.

Bergantung pada operasi yang Anda butuhkan, Anda mungkin bisa lolos dengan presisi tunggal. Inilah idenya:

  • simpan semua nilai yang Anda pedulikan dalam presisi tunggal
  • untuk melakukan operasi:
    • perluas input hingga presisi ganda
    • lakukan operasi dengan presisi ganda
    • hasil konversi kembali ke presisi tunggal

Masalah besar dengan x87 adalah bahwa perhitungan mungkin dilakukan dalam akurasi 53-bit atau 64-bit tergantung pada flag presisi dan apakah register tumpah ke memori. Tetapi untuk banyak operasi, melakukan operasi dengan presisi tinggi dan mengembalikan ke presisi yang lebih rendah akan menjamin jawaban yang benar, yang menyiratkan bahwa jawabannya akan dijamin sama pada semua sistem. Apakah Anda mendapatkan presisi ekstra tidak masalah, karena Anda memiliki cukup presisi untuk menjamin jawaban yang tepat dalam kedua kasus tersebut.

Operasi yang harus bekerja dalam skema ini: penjumlahan, pengurangan, perkalian, pembagian, sqrt. Hal-hal seperti dosa, exp, dll. Tidak akan berfungsi (hasil biasanya akan cocok tetapi tidak ada jaminan). "Kapan pembulatan ganda tidak berbahaya?" Referensi ACM (dibayar reg req.)

Semoga ini membantu!


2
Ini juga masalah yang .NET 5, atau 6, atau 42, mungkin tidak menggunakan mode perhitungan x87 lagi. Tidak ada standar yang mengharuskannya.
Eric J.

5

Seperti yang sudah dinyatakan oleh jawaban lain: Ya, ini adalah masalah di C # - bahkan ketika tetap menggunakan Windows murni.

Adapun solusinya: Anda dapat mengurangi (dan dengan sedikit usaha / kinerja hit) menghindari masalah sepenuhnya jika Anda menggunakan BigIntegerkelas bawaan dan menskalakan semua perhitungan ke presisi yang ditetapkan dengan menggunakan penyebut umum untuk setiap perhitungan / penyimpanan angka-angka tersebut.

Seperti yang diminta oleh OP - mengenai kinerja:

System.Decimalmewakili angka dengan 1 bit untuk tanda dan Integer 96 bit dan "skala" (mewakili di mana titik desimal berada). Untuk semua perhitungan, Anda membuatnya harus beroperasi pada struktur data ini dan tidak dapat menggunakan instruksi floating point yang tertanam dalam CPU.

The BigInteger"solusi" melakukan sesuatu yang serupa - hanya itu Anda dapat menentukan berapa banyak digit yang Anda butuhkan / inginkan ... mungkin Anda ingin hanya 80 bit atau 240 bit presisi.

Kelambatan datang selalu karena harus mensimulasikan semua operasi pada angka ini melalui instruksi integer-only tanpa menggunakan instruksi built-in CPU / FPU yang pada gilirannya menyebabkan lebih banyak instruksi per operasi matematika.

Untuk mengurangi hit kinerja ada beberapa strategi - seperti QNumbers (lihat jawaban dari Jonathan Dickinson - Apakah matematika floating-point konsisten dalam C #? Mungkinkah? ) Dan / atau caching (misalnya perhitungan trigonometri ...) dll.


1
Perhatikan bahwa BigIntegerhanya tersedia di .Net 4.0.
svick

Dugaan saya adalah bahwa hit kinerja BigIntegermelebihi bahkan kinerja terkena Desimal.
CodesInChaos

Beberapa kali dalam jawaban di sini ada referensi ke hit kinerja menggunakan Decimal(@Jonathan Dickinson - 'anjing lambat') atau BigInteger(@CodeInChaos komentar di atas) - dapatkah seseorang memberikan sedikit penjelasan tentang hit kinerja ini dan apakah / mengapa mereka benar-benar pamit untuk memberikan solusi.
Barry Kaye

@Yahia - terima kasih atas hasil editnya - bacaan yang menarik, namun, bisakah Anda juga memberikan perkiraan-perkiraan-perkiraan mengenai performa permainan tidak menggunakan 'float' yang sedang kita bicarakan 10% lebih lambat atau 10 kali lebih lambat - saya hanya ingin merasakan urutan besarnya tersirat.
Barry Kaye

itu lebih mungkin di bidang 1: 5 dari "hanya 10%"
Yahia

2

Nah, ini akan menjadi upaya pertama saya tentang bagaimana melakukan ini :

  1. Buat proyek ATL.dll yang memiliki objek sederhana di dalamnya untuk digunakan untuk operasi titik mengambang kritis Anda. pastikan untuk mengkompilasinya dengan flag yang dinonaktifkan menggunakan perangkat keras non xx87 untuk melakukan floating point.
  2. Buat fungsi yang memanggil operasi floating point dan kembalikan hasilnya; mulai dari yang sederhana dan kemudian jika itu bekerja untuk Anda, Anda selalu dapat meningkatkan kompleksitas untuk memenuhi kebutuhan kinerja Anda nanti jika perlu.
  3. Letakkan panggilan control_fp di sekitar matematika aktual untuk memastikan bahwa itu dilakukan dengan cara yang sama pada semua mesin.
  4. Referensi perpustakaan baru Anda dan uji untuk memastikan itu berfungsi seperti yang diharapkan.

(Saya yakin Anda hanya dapat mengkompilasi ke .dll 32-bit dan kemudian menggunakannya dengan x86 atau AnyCpu [atau kemungkinan hanya menargetkan x86 pada sistem 64-bit; lihat komentar di bawah].)

Kemudian, dengan asumsi itu berfungsi, apakah Anda ingin menggunakan Mono Saya bayangkan Anda harus dapat mereplikasi perpustakaan pada platform x86 lainnya dengan cara yang serupa (bukan COM tentu saja; walaupun, mungkin, dengan anggur? Sedikit keluar dari daerah saya sekali kita pergi ke sana ...).

Dengan asumsi Anda dapat membuatnya berfungsi, Anda harus dapat mengatur fungsi khusus yang dapat melakukan beberapa operasi sekaligus untuk memperbaiki masalah kinerja, dan Anda akan memiliki matematika titik apung yang memungkinkan Anda untuk mendapatkan hasil yang konsisten di seluruh platform dengan jumlah minimal kode yang ditulis dalam C ++, dan meninggalkan sisa kode Anda dalam C #.


"kompilasi ke .dll 32-bit dan kemudian gunakan ... AnyCpu" Saya pikir ini hanya akan bekerja ketika berjalan pada sistem 32 bit. Pada sistem 64bit, hanya program penargetan yang x86akan dapat memuat 32 bit dll.
CodesInChaos

2

Saya bukan pengembang game, meskipun saya memiliki banyak pengalaman dengan masalah yang sulit secara komputasi ... jadi, saya akan melakukan yang terbaik.

Strategi yang saya akan adopsi pada dasarnya adalah ini:

  • Gunakan metode yang lebih lambat (jika perlu; jika ada cara yang lebih cepat, hebat!), Tetapi dapat diprediksi untuk mendapatkan hasil yang dapat direproduksi
  • Gunakan ganda untuk yang lainnya (mis., Rendering)

Singkatnya adalah: Anda perlu menemukan keseimbangan. Jika Anda menghabiskan rendering 30 ms (~ 33fps) dan hanya 1ms yang melakukan deteksi tabrakan (atau menyisipkan beberapa operasi yang sangat sensitif) - bahkan jika Anda melipattigakan waktu yang diperlukan untuk melakukan aritmatika kritis, dampaknya pada framerate Anda adalah Anda turun dari 33,3fps ke 30,3fps.

Saya sarankan Anda membuat profil segalanya, memperhitungkan berapa banyak waktu yang dihabiskan untuk melakukan masing-masing perhitungan yang terasa mahal, lalu ulangi pengukuran dengan 1 atau lebih metode untuk menyelesaikan masalah ini dan lihat apa dampaknya.


1

Memeriksa tautan di jawaban lain menjelaskan bahwa Anda tidak akan pernah memiliki jaminan apakah floating point "benar" diterapkan atau apakah Anda akan selalu menerima ketepatan tertentu untuk perhitungan tertentu, tetapi mungkin Anda bisa melakukan upaya terbaik dengan (1) memotong semua kalkulasi ke minimum umum (misalnya, jika implementasi yang berbeda akan memberikan Anda 32 hingga 80 bit presisi, selalu memotong setiap operasi hingga 30 atau 31 bit), (2) memiliki tabel beberapa kasus uji pada saat startup (batas kasus menambah, mengurangi, mengalikan, membagi, sqrt, cosinus, dll) dan jika implementasi menghitung nilai yang cocok dengan tabel maka tidak perlu repot melakukan penyesuaian.


selalu memotong setiap operasi hingga 30 atau 31 bit - ini adalah persis apa yang dilakukan floattipe data pada mesin x86 - namun ini akan menyebabkan hasil yang sedikit berbeda dari mesin yang melakukan semua perhitungan mereka hanya menggunakan 32-bit, dan perubahan kecil ini akan menyebar seiring waktu. Karena itu, pertanyaannya.
BlueRaja - Danny Pflughoeft

Jika "N bit presisi" berarti perhitungan apa pun akurat untuk banyak bit itu, dan mesin A akurat hingga 32 bit sedangkan mesin B akurat hingga 48 bit, maka 32 bit pertama dari setiap kalkulasi oleh kedua mesin harus identik. Tidak akan memotong hingga 32 bit atau kurang setelah setiap operasi menjaga kedua mesin tetap sinkron? Jika tidak, apa contohnya?
ID Perlindungan Saksi 44583292

-3

Pertanyaan Anda dalam hal-hal yang cukup sulit dan teknis O_o. Namun saya mungkin punya ide.

Anda yakin tahu bahwa CPU melakukan beberapa penyesuaian setelah operasi apung. Dan CPU menawarkan beberapa instruksi berbeda yang membuat operasi pembulatan berbeda.

Jadi untuk ekspresi, kompiler Anda akan memilih serangkaian instruksi yang membawa Anda ke hasil. Tetapi setiap alur kerja instruksi lainnya, bahkan jika mereka bermaksud untuk menghitung ekspresi yang sama, dapat memberikan hasil lain.

'Kesalahan' yang dilakukan oleh penyesuaian pembulatan akan tumbuh pada setiap instruksi lebih lanjut.

Sebagai contoh, kita dapat mengatakan bahwa pada tingkat perakitan: a * b * c tidak setara dengan * c * b.

Saya tidak sepenuhnya yakin akan hal itu, Anda perlu meminta seseorang yang lebih mengenal arsitektur CPU daripada saya: hal

Namun untuk menjawab pertanyaan Anda: di C atau C ++ Anda dapat memecahkan masalah Anda karena Anda memiliki beberapa kontrol pada kode mesin yang dihasilkan oleh kompiler Anda, namun dalam. NET Anda tidak punya. Jadi selama kode mesin Anda bisa berbeda, Anda tidak akan pernah yakin tentang hasil yang tepat.

Saya ingin tahu bagaimana hal ini dapat menjadi masalah karena variasi tampaknya sangat minim, tetapi jika Anda benar-benar membutuhkan operasi, satu-satunya solusi yang dapat saya pikirkan adalah meningkatkan ukuran register mengambang Anda. Gunakan presisi ganda atau bahkan panjang ganda jika Anda bisa (tidak yakin itu mungkin menggunakan CLI).

Saya harap saya sudah cukup jelas, saya tidak sempurna dalam bahasa Inggris (... sama sekali: s)


9
Bayangkan penembak P2P. Anda menembak seorang pria, Anda memukulnya dan dia mati, tapi itu sangat dekat, Anda hampir ketinggalan. Di sisi lain, PC pria menggunakan perhitungan yang sedikit berbeda dan menghitung yang Anda lewatkan. Apakah Anda melihat masalahnya sekarang? Dalam hal ini, meningkatkan ukuran register tidak akan membantu (setidaknya tidak sepenuhnya). Menggunakan perhitungan yang sama persis di setiap komputer akan.
svick

5
Dalam skenario ini, orang biasanya tidak peduli seberapa dekat hasilnya dengan hasil yang sebenarnya (asalkan masuk akal), tetapi yang penting adalah bahwa itu persis sama untuk semua pengguna.
CodesInChaos

1
Anda benar, saya tidak memikirkan skenario semacam ini. Namun saya setuju dengan @CodeInChaos yang satu ini. Saya tidak menemukan itu benar-benar pintar untuk mengambil keputusan penting dua kali. Ini lebih merupakan masalah arsitektur perangkat lunak. Satu program, aplikasi penembak untuk contoh, harus membuat perhitungan dan mengirimkan hasilnya kepada yang lain. Anda tidak akan pernah memiliki kesalahan dengan cara ini. Anda memiliki hit atau tidak, tetapi hanya satu yang mengambil keputusan. Seperti say @driushkin
AxFab

2
@ Aesgar: Ya, itulah cara sebagian besar penembak bekerja; "otoritas" itu disebut server, dan kami menyebut arsitektur keseluruhan sebagai arsitektur "klien / server". Namun, ada jenis arsitektur lain: peer-to-peer. Di P2P, tidak ada server; melainkan, semua klien harus memverifikasi semua tindakan satu sama lain sebelum sesuatu terjadi. Ini meningkatkan jeda, membuatnya tidak dapat diterima oleh penembak, tetapi sangat mengurangi lalu lintas jaringan, menjadikannya sempurna untuk gim di mana jeda kecil (~ 250 ms) dapat diterima, tetapi menyinkronkan seluruh kondisi gim tidak. Yaitu, game RTS seperti C&C dan Starcraft menggunakan P2P.
BlueRaja - Danny Pflughoeft

5
Dalam gim p2p Anda tidak memiliki mesin tepercaya untuk diandalkan. Jika Anda mengizinkan satu stasiun untuk memutuskan apakah pelurunya menghantam atau tidak, Anda membuka kemungkinan kecurangan klien. Selain itu, tautannya bahkan tidak dapat menangani jumlah data yang kadang-kadang menghasilkan - permainan bekerja dengan mengirimkan pesanan daripada hasilnya. Saya bermain game RTS dan sering kali saya melihat begitu banyak sampah terbang di sekitar tidak ada cara itu bisa dikirim melalui uplink rumah tangga normal.
Loren Pechtel
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.