Kayan noktalı matematik C # 'da tutarlı mı? Olabilir mi?


155

Hayır, bu başka bir "Neden (1 / 3.0) * 3! = 1" sorusu değil.

Son zamanlarda kayan noktalar hakkında çok şey okudum; özellikle, aynı hesaplamanın farklı mimarilerde veya optimizasyon ayarlarında nasıl farklı sonuçlar verebileceği .

Bu, tekrarları depolayan veya eşler arası ağa bağlı (sunucu-istemcinin aksine) video oyunları için bir sorundur , bu da programı her çalıştırdıklarında tam olarak aynı sonuçları üreten tüm istemcilere güvenir - birinde küçük bir tutarsızlık kayan nokta hesaplama farklı makinelerde çok farklıydı oyun durumuna yol açabilir (hatta veya aynı makinede! )

Bu , IEEE-754'ü "takip eden" işlemciler arasında bile olur , çünkü bazı işlemciler (x86) çift ​​genişletilmiş hassasiyet kullanır . Yani, tüm hesaplamaları yapmak için 80 bit kayıt kullanırlar, daha sonra 64 veya 32 bit olarak kesilirler, bu da hesaplamalar için 64 veya 32 bit kullanan makinelerden farklı yuvarlama sonuçlarına yol açar.

Bu sorun çevrimiçi birkaç çözüm gördüm, ama tüm C ++, C # için:

  • (Windows), (Linux?) Veya (BSD) doublekullanarak çift ​​genişletilmiş kesinlik modunu devre dışı bırakın (tüm hesaplamalar IEEE-754 64 bit kullanır )._controlfp_s_FPU_SETCWfpsetprec
  • Her zaman aynı derleyiciyi aynı optimizasyon ayarlarıyla çalıştırın ve tüm kullanıcıların aynı CPU mimarisine sahip olmasını gerektirir (platformlar arası oynama yok). Benim "derleyici" aslında program her çalıştırıldığında farklı optimize edebilir JIT olduğundan, bunun mümkün olduğunu düşünmüyorum.
  • Sabit noktalı aritmetik kullanın floatve doubletamamen ve kaçının . decimalbu amaçla çalışır, ancak çok daha yavaş olur ve hiçbir System.Mathkütüphane işlevi bunu desteklemez.

Peki, bu bile C # bir sorun mu? Ya sadece Windows'u desteklemeyi amaçlıyorsam (Mono'yu değil)?

Eğer öyleyse, programımı normal çift hassasiyette çalışmaya zorlamanın bir yolu var mı?

Değilse, kayan nokta hesaplamalarının tutarlı kalmasına yardımcı olacak herhangi bir kütüphane var mı?


Bu soruyu gördüm , ancak her cevap ya sorunu çözümsüz olarak tekrarlıyor ya da "görmezden gel" diyor ki bu bir seçenek değil. Sordum gamedev üzerinde benzer bir soru , ama (çünkü seyirci) cevapların çoğu C ++ yönelik gibi görünüyor.
BlueRaja - Dany Pflughoeft

1
bir cevap değil, ama eminim çoğu alanda sisteminizi tüm paylaşılan durum belirleyici olacak şekilde tasarlayabilirsiniz ve bundan dolayı önemli bir performans düşüşü olmaz
driushkin

1
@Peter .net için hızlı kayan nokta öykünmesi biliyor musunuz?
CodesInChaos

1
Java bu sorundan muzdarip mi?
Josh

3
@Josh: Java, strictfptüm hesaplamaları genişletilmiş boyuttan ziyade belirtilen boyutta ( floatveya double) yapılmaya zorlayan bir anahtar kelimeye sahiptir . Ancak Java'nın hala IEE-754 desteği ile ilgili birçok sorunu var. Çok (çok, çok) az programlama dili IEE-754'ü iyi destekler.
porges

Yanıtlar:


52

.Net içinde normal kayan noktaları deterministik yapmanın bir yolunu bilmiyorum. JITter'ın farklı platformlarda (veya .net'in farklı sürümleri arasında) farklı davranan kodlar oluşturmasına izin verilir. Böylece floatdeterministik .net kodunda normal s kullanmak mümkün değildir.

Düşündüğüm geçici çözümler:

  1. FixedPoint32'yi C # ile uygulayın. Bu çok zor olmasa da (yarı bitmiş bir uygulama var) çok küçük değerler aralığı onu rahatsız edici hale getiriyor. Her zaman dikkatli olmalısınız, böylece ne taşar ne de çok fazla hassasiyet kaybedersiniz. Sonunda bunu tamsayıları doğrudan kullanmaktan daha kolay bulmadım.
  2. FixedPoint64'ü C # ile uygulayın. Bunu yapmakta zorlandım. Bazı işlemler için 128bit ara tamsayıları yararlı olacaktır. Ancak .net böyle bir tür sunmuyor.
  3. Özel bir 32 bit kayan nokta uygulayın. Bir BitScanReverse içsel eksikliği, bunu uygularken birkaç sıkıntıya neden olur. Ama şu anda bunun en umut verici yol olduğunu düşünüyorum.
  4. Matematik işlemleri için yerel kodu kullanın. Her matematik işleminde bir delege çağrısının yükünü azaltır.

32 bit kayan nokta matematiğinin bir yazılım uygulamasını başlattım. 2.66GHz i3 cihazımda saniyede yaklaşık 70 milyon ekleme / çarpma yapabilir. https://github.com/CodesInChaos/SoftFloat . Açıkçası hala çok eksik ve buggy.


2
"int sınırsız" boyutlu tamsayı kullanılabilir BigInteger yerel int veya uzun kadar hızlı olmasa da, bu yüzden .NET böyle bir tür sunuyor (inanıyorum F # için yaratılmış ama C # kullanılabilir)
Rune FS

Başka bir seçenek .NET için GNU MP sarıcıdır . Bu bir çevrede sarıcı var GNU Çoklu Hassas Kütüphanesi destekler "inifinite" hassas tamsayılar, rationals (kesirler) ve kayan noktalı sayılar.
Cole Johnson

2
Bunlardan herhangi birini yapacaksanız, decimalilk önce deneyebilirsiniz , çünkü bunu yapmak çok daha basittir. Sadece eldeki görev için çok yavaşsa, diğer yaklaşımlar düşünmeye değer olacaktır.
Roman Starkov

Kayan noktaların belirleyici olduğu özel bir durum öğrendim. Elde ettiğim açıklama: Çarpma / bölme için, FP numaralarından biri iki sayının gücü (2 ^ x) ise, anlamlı / mantis hesaplama sırasında değişmez. Sadece üs değişecek (nokta hareket edecektir). Yani yuvarlama asla olmayacak. Sonuç deterministik olacaktır.
zigzag

Örnek: 2 ^ 32 gibi bir sayı (üs: 32, mantis: 1) olarak temsil edilir. Bunu başka bir şamandıra (exp, man) ile çarparsak, sonuç (exp + 32, man * 1) olur. Bölme için sonuç (expo - 32, adam * 1). Mantis'i 1 ile çarpmak mantisi değiştirmez, bu yüzden kaç biti olduğu önemli değildir.
zigzag

28

C # spesifikasyonu (§4.1.6 Kayan nokta türleri) özellikle kayan nokta hesaplamalarının, sonuçtan daha yüksek hassasiyet kullanılarak yapılmasına izin verir. Yani, hayır, bu hesaplamaları doğrudan .Net'te belirleyici hale getirebileceğinizi sanmıyorum. Diğerleri çeşitli geçici çözümler önerdi, böylece deneyebilirsiniz.


9
Ben sadece bir derlenmiş derlemeler dağıtırsanız C # belirtimi gerçekten önemli olmadığını fark ettim. Yalnızca kaynak uyumluluğu istendiğinde önemlidir. Asıl önemli olan CLR spesifikasyonu. Ama eminim ki C # garantileri kadar zayıftır.
CodesInChaos

doubleBir işlemden sonra her seferinde döküm , istenmeyen bitleri çıkarır ve tutarlı sonuçlar verir mi?
IllidanS4, Monica'yı

2
@ IllidanS4 Bunun tutarlı sonuçları garanti edeceğini sanmıyorum.
svick

14

Aşağıdaki sayfa, bu tür işlemlerin mutlak taşınabilirliğine ihtiyaç duyduğunuz durumlarda faydalı olabilir. Kayan nokta işlemlerini taklit etmek için yazılım da dahil olmak üzere IEEE 754 standardının uygulamalarını test etmek için yazılımı tartışır. Ancak çoğu bilgi muhtemelen C veya C ++ 'a özgüdür.

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

Sabit nokta hakkında bir not

İkili sabit nokta sayıları, dört temel aritmetik işlemden de anlaşılacağı gibi, kayan nokta yerine kullanılabilir:

  • Toplama ve çıkarma önemsizdir. Tamsayılarla aynı şekilde çalışırlar. Sadece ekleyin veya çıkarın!
  • İki sabit nokta sayısını çarpmak için, iki sayıyı çarpın ve sonra tanımlanan kesirli bit sayısını sağa kaydırın.
  • İki sabit nokta numarasını bölmek için, temettüyü tanımlanan kesirli bit sayısını sola kaydırın, sonra bölenle bölün.
  • Bu makalenin dördüncü bölümü , ikili sabit nokta sayılarının uygulanmasına ilişkin ek kılavuz içermektedir.

İkili sabit nokta sayıları int, long ve BigInteger gibi herhangi bir tamsayı veri tipine ve uint ve ulong CLS uyumlu olmayan tiplere uygulanabilir.

Başka bir yanıtta önerildiği gibi, sinüs, kosinüs, karekök vb. Gibi karmaşık işlevlerin uygulanmasına yardımcı olmak için tablodaki her öğenin ikili sabit nokta sayısı olduğu arama tablolarını kullanabilirsiniz. Arama tablosu sabit nokta numarasından daha az ayrıntılıysa, arama tablosunun ayrıntı düzeyinin yarısını girişe ekleyerek girdinin yuvarlanması önerilir:

// 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
Bunu sourceforge veya github gibi açık kaynaklı bir kod proje sitesine yüklemelisiniz. Bu, bulmayı kolaylaştırır, katkıda bulunmayı kolaylaştırır, özgeçmişinizi koymayı kolaylaştırır. Ayrıca, birkaç kaynak kodu ipucu (yoksaymaktan çekinmeyin): Sabitler constyerine kullanın static, böylece derleyici bunları optimize edebilir; üye işlevlerini statik işlevlere tercih edin (böylece örneğin myDouble.LeadingZeros()yerine çağırabiliriz IntDouble.LeadingZeros(myDouble)); tek harfli değişken adlarından kaçınmaya çalışın ( MultiplyAnyLengthörneğin, 9 tane vardır, takip etmeyi çok zorlaştırır)
BlueRaja - Danny Pflughoeft

Kullanırken dikkatli olun uncheckedve non-CLS uyumlu gibi türleri ulong, uintbu yüzden onları aslında olabilir kullanılarak, bu kadar nadiren kullanılır, çünkü JIT kadar agresif bunları optimize değil - vs. hız amaçlı yavaş gibi normal türlerini kullanmak yerine longve int. Ayrıca, C # vardır bu projenin büyük fayda sağlayacağı operatör aşırı yüklemesine . Son olarak, ilişkili birim testleri var mı? Bu küçük şeylerin yanı sıra, inanılmaz iş Peter, bu gülünç etkileyici!
BlueRaja - Danny Pflughoeft

Yorumlar için teşekkürler. Kod üzerinde birim testleri yapıyorum. Bununla birlikte, şimdilik serbest bırakılamayacak kadar genişler. Birden fazla test yazmayı kolaylaştırmak için birim test yardımcı rutinleri bile yazıyorum. Şimdilik kodları Java'ya çevirmeyi planladığım için aşırı yüklenmiş operatörleri kullanmıyorum.
Peter O.

2
Komik olan şey, blogunuza gönderdiğimde blogun size ait olduğunu fark etmedim. Google + 'yı denemeye karar verdim ve C # kıvılcımında bu blog girişini önerdi. Ben de "İkimiz için böyle bir şeyi aynı anda yazmaya başlamak ne kadar büyük bir tesadüf" diye düşündüm. Ama elbette aynı tetikleyicimiz vardı :)
CodesInChaos

1
Bunu Java'ya neden taşımaktan rahatsız oluyorsun? Java zaten üzerinden deterministik kayan nokta matematik garanti strictfp.
Antimon

9

Bu C # için bir sorun mu?

Evet. Farklı mimariler endişelerinizin en azıdır, farklı çerçeveler vb. Şamandıra temsillerindeki yanlışlıklar nedeniyle sapmalara yol açabilir - aynı yanlışlıklar olsa bile (örneğin bir makinede daha yavaş bir GPU hariç).

System.Decimal kullanabilir miyim?

Yapamamanın bir sebebi yok, ama köpek yavaş.

Programımı çift kesinlikli çalışmaya zorlamanın bir yolu var mı?

Evet. CLR çalışma zamanını kendiniz barındırın ; ve CorBindToRuntimeEx çağrılmadan önce tüm nessecary çağrıları / bayrakları (kayan nokta aritmetiği davranışını değiştiren) C ++ uygulamasında derleyin.

Kayan nokta hesaplamalarının tutarlı kalmasına yardımcı olacak herhangi bir kütüphane var mı?

Bildiğim kadarıyla hayır.

Bunu çözmenin başka bir yolu var mı?

Bu sorunu daha önce ele aldım, fikir kullanmak çözdüm QNumbers . Bunlar sabit nokta olan bir tür realitedir; ancak taban-10'da (ondalık) sabit nokta değil - taban-2'de (ikili); bu nedenle üzerlerindeki matematiksel ilkeler (toplama, alt, çoklu, div) naif taban-10 sabit noktalarından çok daha hızlıdır; özellikle nher iki değer için de aynıysa (ki bu durumda olur). Ayrıca, integral oldukları için her platformda iyi tanımlanmış sonuçlara sahiptirler.

Kare hızının bunları hala etkileyebileceğini, ancak o kadar kötü olmadığını ve senkronizasyon noktaları kullanılarak kolayca düzeltilebileceğini unutmayın.

QNumbers ile daha fazla matematiksel fonksiyon kullanabilir miyim?

Evet, bunu yapmak için bir ondalık basamak döndürün. Ayrıca, trig (sin, cos) işlevleri için gerçekten arama tabloları kullanmalısınız ; çünkü bunlar farklı platformlarda gerçekten farklı sonuçlar verebilir - ve bunları doğru şekilde kodlarsanız, QNumbers'ı doğrudan kullanabilirler.


3
Çerçevelerin sorunla neden bahsettiğinden emin değilim. Açıkçası sabit bir güncelleme hızına sahip olmak istersiniz (örneğin buraya bakın ) - ekran-kare hızı ile aynı olup olmadığı önemsizdir. Yanlışlıklar tüm makinelerde aynı olduğu sürece iyiyiz. Üçüncü cevabınızı hiç anlamıyorum.
BlueRaja - Danny Pflughoeft

@BlueRaja: "Programımı çift kesinlikte çalışmaya zorlamanın bir yolu var mı?" ya son derece karmaşık olan tüm Ortak Dil Çalışma Zamanı'nın yeniden uygulanması ya da kullanıcı shelleybutterfly yanıtında ima edildiği gibi C # uygulamasından C ++ DLL'e yerel çağrılar kullanılması anlamına gelir. "QNumbers" ı cevabımda ima ettiği gibi sadece ikili sabit nokta sayıları olarak düşün (Şu ana kadar ikili sabit nokta numaralarının "QNumbers" olarak adlandırıldığını görmemiştim.)
Peter O.

@Pieter O. Çalışma zamanını yeniden uygulamanıza gerek yoktur. Şirketimde çalıştığım sunucu, CLR çalışma zamanını yerel bir C ++ uygulaması olarak barındırıyor (SQL Server da öyle). Size Google CorBindToRuntimeEx öneririm.
Jonathan Dickinson

@BlueRaja, söz konusu oyuna bağlı. Tüm oyunlara sabit kare hızı adımları uygulamak uygun bir seçenek değildir - çünkü AOE algoritması yapay gecikme sağlar; örneğin bir FPS'de kabul edilemez.
Jonathan Dickinson

1
@Jonathan: Bu yalnızca girdiyi gönderen eşler arası oyunlarda bir sorundur - bunlar için sabit bir güncelleme oranına sahip olmanız gerekir . Çoğu FPS bu şekilde çalışmaz, ancak birkaçının sabit bir güncelleme hızına sahip olması gerekir. Bu soruya bakın .
BlueRaja - Danny Pflughoeft

6

Bu biraz eski MSDN blog girişine göre JIT kayan nokta için SSE / SSE2 kullanmayacak, hepsi x87. Bu nedenle, bahsettiğiniz gibi modlar ve bayraklar hakkında endişelenmeniz gerekiyor ve C # 'da kontrol etmek mümkün değil. Bu nedenle, normal kayan nokta işlemlerinin kullanılması, programınız için her makinede aynı sonucu garanti etmez.

Çift hassasiyette hassas tekrar üretilebilirlik elde etmek için yazılım kayan nokta (veya sabit nokta) emülasyonu yapmanız gerekecektir. Bunu yapmak için C # kitaplıkları bilmiyorum.

İhtiyacınız olan işlemlere bağlı olarak, tek bir hassasiyetle kurtulabilirsiniz. İşte fikir:

  • önem verdiğiniz tüm değerleri tek bir hassasiyetle saklayın
  • bir işlem gerçekleştirmek için:
    • girişleri iki kat hassasiyetle genişlet
    • çift ​​hassasiyette çalışma
    • sonucu tekrar tek duyarlığa dönüştür

X87 ile ilgili en büyük sorun, hassas bayrağa ve kaydın belleğe dökülüp dökülmediğine bağlı olarak hesaplamaların 53 bit veya 64 bit doğrulukta yapılabilmesidir. Ancak birçok işlem için, işlemi yüksek hassasiyette gerçekleştirmek ve daha düşük hassasiyete geri döndürmek doğru cevabı garanti eder, bu da cevabın tüm sistemlerde aynı olacağı garanti edilir. Her iki durumda da doğru cevabı garanti etmek için yeterli hassasiyete sahip olduğunuz için ekstra hassasiyet elde edip etmediğiniz önemli değildir.

Bu şemada çalışması gereken işlemler: toplama, çıkarma, çarpma, bölme, sqrt. Günah, exp vb. Şeyler işe yaramaz (sonuçlar genellikle eşleşir, ancak garanti yoktur). "Çift yuvarlama ne zaman zararsız?" ACM Referansı (ücretli kayıt gereksinimi)

Bu yardımcı olur umarım!


2
.NET 5 veya 6 veya 42'nin artık x87 hesaplama modunu kullanmaması da bir sorundur. Standartta bunu gerektiren hiçbir şey yoktur.
Eric J.

5

Zaten diğer cevaplar tarafından belirtildiği gibi: Evet, bu saf Windows kalırken bile C # bir sorundur.

Bir çözüme gelince: BigIntegerYerleşik sınıfı kullanırsanız ve bu tür sayıların herhangi bir hesaplanması / saklanması için ortak bir payda kullanarak tüm hesaplamaları tanımlanmış bir hassasiyete ölçeklendirirseniz , sorunu tamamen azaltabilirsiniz (ve biraz çaba / performans isabetiyle) .

OP tarafından talep edildiği üzere - performansla ilgili olarak:

System.Decimalbir işaret için 1 bit ve 96 bit Tam sayı ve bir "ölçek" olan sayıyı temsil eder (ondalık noktasının nerede olduğunu gösterir). Yaptığınız tüm hesaplamalar için bu veri yapısı üzerinde çalışmalıdır ve CPU'da yerleşik kayan nokta komutlarını kullanamaz.

BigInteger"Çözüm" benzer bir şey yapar - belki sadece 80 bit veya kesinlik 240 bit istiyorum ... sen lüzum / ne kadar istediğini rakam tanımlayabilirsiniz sadece o.

Yavaşlık, CPU / FPU yerleşik talimatlarını kullanmadan bu sayıdaki tüm işlemleri sadece tamsayı talimatları ile simüle etmek zorunda kalmaktan kaynaklanır ve bu da matematik işlemi başına çok daha fazla yönerge sağlar.

Performans vuruşunu azaltmak için QNumbers (Jonathan Dickinson'un cevabına bakınız) gibi kayan nokta matematiği C # 'da tutarlı mı? Olabilir mi? ) Ve / veya önbellekleme (örneğin trig hesaplamaları ...) vb.


1
BigIntegerYalnızca .Net 4.0'da kullanılabilir olduğunu unutmayın .
svick

Benim tahminim BigIntegerperformans isabetinin Ondalık isabetin performansını bile aşması.
CodesInChaos

Buradaki cevaplarda birkaç kez (@Jonathan DecimalDickinson - 'köpek yavaş') veya BigInteger(yukarıdaki @CodeInChaos yorumu ) kullanmanın performans hitine atıfta bulunuluyor - birisi bu performans hitleri hakkında biraz açıklama yapabilir mi ve / Neden bir çözüm sunmaya gerçekten gösterişçi olduklarını.
Barry Kaye

@Yahia - düzenleme için teşekkür ederim - ilginç okuma, ancak, sadece bir top parkı kullanmayan 'float' performans isabeti ile ilgili bir tahmin verebilir misiniz% 10 daha yavaş veya 10 kat daha yavaş - ben sadece ima edilen büyüklük sırası hakkında bir fikir edinmek istiyorum.
Barry Kaye

1: 5 alanında "sadece% 10" dan daha fazla likeley
Yahia

2

İşte bunu nasıl yapacağımla ilgili ilk denemem :

  1. Kritik kayan nokta işlemleriniz için kullanılacak basit bir nesneye sahip bir ATL.dll projesi oluşturun. kayan nokta yapmak için xx87 olmayan herhangi bir donanım kullanmayı devre dışı bırakan bayraklarla derlediğinizden emin olun.
  2. Kayan nokta işlemlerini çağıran ve sonuçları döndüren işlevler oluşturun; basit başlayın ve daha sonra sizin için çalışıyorsa, daha sonra gerekirse performans ihtiyaçlarınızı karşılamak için karmaşıklığı her zaman artırabilirsiniz.
  3. Tüm makinelerde aynı şekilde yapıldığından emin olmak için control_fp çağrılarını gerçek matematiğin etrafına koyun.
  4. Yeni kitaplığınıza bakın ve beklendiği gibi çalıştığından emin olmak için test edin.

(Ben sadece bir 32-bit .dll derlemek ve daha sonra x86 veya AnyCpu ile kullanabilirsiniz inanıyorum [veya muhtemelen sadece 64-bit bir sistemde x86 hedefleme; aşağıdaki açıklamaya bakın].)

Sonra, işe yaradığını varsayarsak, Mono'yu kullanmak istiyorsanız, kütüphaneyi diğer x86 platformlarında benzer bir şekilde çoğaltmanız gerektiğini hayal edin (COM değil elbette; belki de şarapla mı? Biz oraya gitmek olsa ...).

İşe yarayabileceğinizi varsayarsak, herhangi bir performans sorununu gidermek için aynı anda birden fazla işlem yapabilen özel işlevler ayarlayabilmeniz gerekir ve platformlar arasında minimum miktarda tutarlı sonuçlar elde etmenizi sağlayan kayan nokta matematiğiniz olur. C ++ ile yazılmış ve kodunuzun geri kalanını C # 'da bırakarak.


"32-bit .dll için derleyin ve sonra kullanın ... AnyCpu" Bence bu sadece 32-bit bir sistem üzerinde çalışırken çalışacaktır. 64bit sistemde sadece bir program hedefleme x8632 bit dll yükleyebilecektir.
CodesInChaos

2

Ben bir oyun geliştiricisi değilim, ancak hesaplama açısından zor problemlerle ilgili çok fazla deneyime sahibim ... bu yüzden elimden geleni yapacağım.

Benimsediğim strateji aslında şudur:

  • Daha yavaş (gerekirse; daha hızlı bir yol varsa, harika!), Ancak tekrarlanabilir sonuçlar elde etmek için öngörülebilir bir yöntem kullanın
  • Diğer her şey için çift kullanın (örneğin, oluşturma)

Bunun kısa ve özü şudur: bir denge bulmanız gerekir. 30ms render (~ 33fps) ve yalnızca 1ms çarpışma algılaması yapıyorsanız (veya diğer yüksek hassasiyetli işlemleri eklerseniz) - kritik aritmetiği yapmak için gereken süreyi üç katına çıkarsanız bile, kare hızınız üzerindeki etkisi 33.3fps'den 30.3fps'ye düşer.

Her şeyi profillemenizi, göze çarpan pahalı hesaplamaların her birini yapmak için ne kadar zaman harcadığını hesaba katmanızı, ardından ölçümleri bu sorunu çözmek için 1 veya daha fazla yöntemle tekrarlayın ve etkisinin ne olduğunu görün.


1

Diğer cevaplardaki bağlantıları kontrol etmek, kayan noktanın "doğru" uygulandığını veya belirli bir hesaplama için her zaman belirli bir hassasiyet alıp alamayacağınızı asla garanti edemeyeceğinizi açıkça ortaya koyar, ancak belki de en iyi çabayı gösterebilirsiniz. (1) tüm hesaplamaları ortak bir minimumda kesmek (örneğin, farklı uygulamalar size 32 ila 80 bit hassasiyet verecekse, her işlemi her zaman 30 veya 31 bite kesecekse), (2) başlangıçta birkaç test vakasının bir tablosuna sahip olun (sınırda toplama, çıkarma, çarpma, bölme, sqrt, kosinüs, vb.) ve uygulama tablo ile eşleşen değerleri hesaplarsa, herhangi bir ayarlama yapmayı zahmet etmeyin.


her işlemi her zaman 30 veya 31 bite keser - bu, floatveri türünün x86 makinelerde yaptığı şeydir - ancak bu, tüm hesaplamalarını sadece 32 bit kullanarak yapan makinelerden biraz farklı sonuçlara neden olur ve bu küçük değişiklikler zamanla yayılır. Dolayısıyla soru.
BlueRaja - Danny Pflughoeft

"N hassaslık biti" herhangi bir hesaplamanın birçok bit için doğru olduğu anlamına gelirse ve makine B 48 bit için doğruyken, makine A 32 bit için doğruysa, her iki makine tarafından herhangi bir kireçin ilk 32 biti aynı olmalıdır. Her işlemden sonra 32 bite veya daha az kısaltma, her iki makineyi de tam olarak senkronize tutar mı? Değilse, örnek nedir?
Tanık Koruma Kimliği 44583292

-3

Sorunuz oldukça zor ve teknik şeyler O_o. Ancak bir fikrim olabilir.

Herhangi bir değişken işlemden sonra CPU'nun bazı ayarlamalar yaptığından emin olabilirsiniz. Ve CPU farklı yuvarlama işlemi yapan birkaç farklı talimat sunar.

Yani bir ifade için, derleyiciniz sizi bir sonuca götürecek bir dizi talimat seçecektir. Ancak aynı ifadeyi hesaplamak isteseler bile diğer talimat iş akışları başka bir sonuç sağlayabilir.

Yuvarlama ayarlamasıyla yapılan 'hatalar' her bir talimatta büyüyecektir.

Bir örnek olarak, bir montaj seviyesinde şunu söyleyebiliriz: a * b * c, * c * b'ye eşdeğer değildir.

Bundan tam olarak emin değilim, CPU mimarisini benden çok daha fazla bilen birini istemeniz gerekecek: p

Ancak sorunuzu cevaplamak için: C veya C ++ 'da sorununuzu çözebilirsiniz, çünkü derleyiciniz tarafından oluşturulan makine kodu üzerinde bir miktar kontrole sahipsiniz, ancak .NET'te hiç yoktur. Makine kodunuz farklı olabildiği sürece, kesin sonuçtan asla emin olmayacaksınız.

Bunun hangi şekilde bir sorun olabileceğini merak ediyorum, varyasyon çok az görünüyor, ancak gerçekten doğru bir işleme ihtiyacınız varsa, aklıma gelen tek kayıt çözümünüzün kayan kayıtlarınızın boyutunu artırmak olacaktır. Mümkünse çift duyarlık ve hatta uzun çift kullanın (CLI kullanarak bunun mümkün olduğundan emin olun).

Umarım yeterince netim, İngilizce'de mükemmel değilim (... hiç: s)


9
Bir P2P atıcı düşünün. Bir adama ateş edersin, ona vurursun ve ölür, ama çok yakın, neredeyse özlüyorsun. Diğer adamın bilgisayarında biraz farklı hesaplamalar kullanır ve kaçırdığınız hesaplamaları yapar. Sorunu şimdi görüyor musun? Bu durumda, kayıtların boyutunu artırmak yardımcı olmaz (en azından tamamen değil). Her bilgisayarda tam olarak aynı hesaplamayı kullanacaktır.
svick

5
Bu senaryoda genellikle sonucun gerçek sonuca ne kadar yakın olduğu umursamaz (makul olduğu sürece), ancak önemli olan bunun tüm kullanıcılar için tamamen aynı olmasıdır.
CodesInChaos

1
Haklısın, bu tür bir senaryo hakkında düşünmedim. Ancak ben bu konuda @CodeInChaos katılıyorum. Önemli bir kararın iki kere alınacağını gerçekten zekice bulamadım. Bu daha çok bir yazılım mimarisi meselesidir. Bir program, atıcıya örnek uygulama, hesaplama yapmalı ve sonucu diğerlerine göndermelidir. Bu şekilde asla hatalarınız olmayacak. Bir vuruşun var ya da değil, ama sadece biri kararını alır. Like say @driushkin
AxFab

2
@Aesgar: Evet, atıcıların çoğu böyle çalışır; sunucuya "yetki" denir ve genel mimariye "istemci / sunucu" mimarisi diyoruz. Ancak, başka bir mimari türü daha vardır: eşler arası. P2P'de sunucu yoktur; bunun yerine, tüm istemciler herhangi bir şey gerçekleşmeden önce tüm işlemleri birbirleriyle doğrulamalıdır. Bu, gecikmeyi artırır ve atıcılar için kabul edilemez hale getirir, ancak ağ trafiğini büyük ölçüde azaltır, bu da küçük bir gecikmenin (~ 250ms) kabul edilebilir olduğu oyunlar için mükemmel olmasını sağlar, ancak tüm oyun durumunu senkronize etmez. Yani, C&C ve Starcraft gibi RTS oyunları P2P kullanıyor.
BlueRaja - Danny Pflughoeft

5
Bir p2p oyununda güvenebileceğiniz güvenilir bir makineniz yok. Bir istasyonun mermisinin vurup vurmadığına karar vermesine izin verirseniz, bir müşterinin hile yapma olasılığını açarsınız. Dahası, bağlantılar bazen sonuçlanan veri miktarını bile işleyemez - oyunlar sonuçlar yerine siparişleri göndererek çalışır. RTS oyunları oynuyorum ve birçok kez etrafta çok fazla önemsiz uçuşun olduğunu gördüm, normal ev uplink'leri üzerinden gönderilemez.
Loren Pechtel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.