Kayan nokta yuvarlama hataları için çözümler


18

Matematiksel hesaplamalar ile ilgilenen bir uygulama oluştururken, belirli sayıların yuvarlama hatalarına neden olduğu sorunuyla karşılaştım.

Bunu anlamak ise kayan nokta tam değil , sorunun nasıl hesaplamalar değil herhangi bir sorun neden yapar noktası yuvarlama kayan üzerlerinde önceden oluşturulmaktadır zaman emin olmak için tam sayılarla başa çıkıyorsunuz?


2
Karşılaştığınız belirli bir sorun var mı? Test yapmanın birçok yolu vardır, bazı problemler için sorun yoktur. Birden fazla yanıtı olabilecek sorular S ve C formatı için yetersizdir. Yaşadığınız problemi, fikirler ve öneriler için bir ağ oluşturmak yerine tek bir doğru cevaba sahip olacak şekilde tanımlayabilmeniz en iyisidir.

Matematiksel hesaplamalar içeren bir Yazılım Uygulaması geliştiriyorum. NUNIT veya JUNIT testinin iyi olacağını anlıyorum, ancak Matematiksel Hesaplamalar ile ilgili sorunlara nasıl yaklaşılacağı hakkında bir fikir edinmek isterim.
JNL

1
Test edeceğiniz bir hesaplamaya örnek verebilir misiniz? Tipik olarak ham matematiği birim test etmek olmaz (kendi sayısal türlerinizi test etmiyorsanız), ancak böyle bir şeyi test etmek distanceTraveled(startVel, duration, acceleration)test edilir.

Bir örnek ondalık noktalarla ilgilidir. Örneğin, dist x-0 ila x = 14.589 için özel ayarlara sahip bir duvar inşa ettiğimizi ve ardından x = 14.589 ile x = duvarın ucunda bazı düzenlemeler yaptığımızı varsayalım. İkili dönüştürülürken mesafe .589 aynı değildir .... Özellikle bazı mesafeler eklersek ... 14.589 + 0.25 gibi ikili 14.84 eşit olmayacak .... Umarım kafa karıştırıcı değil mi?
JNL

1
@MichaelT Soruyu düzenlediğiniz için teşekkür ederiz. Çok yardımcı oldu. Bu konuda yeni olduğum için, soruları çerçevelemek için çok iyi değil. :) ... Ama yakında iyi olacak.
JNL

Yanıtlar:


22

Kayan nokta yuvarlaması olmayan alternatif sayısal türler oluşturmak için üç temel yaklaşım vardır. Bunların ortak teması, çeşitli şekillerde tamsayı matematik kullanmalarıdır.

Rational

Sayıyı bir parça ve payda ile bir bütün olarak ve rasyonel sayı olarak temsil edin. Sayı 15.589şu şekilde temsil edilir:w: 15; n: 589; d:1000 .

0.25'e (ki bu w: 0; n: 1; d: 4) eklendiğinde , bu LCM'nin hesaplanmasını ve ardından iki sayının eklenmesini içerir. Bu, birçok durum için iyi çalışır, ancak birbirine göre nispeten asal olan birçok rasyonel sayıyla çalışırken çok büyük sayılara neden olabilir.

Sabit nokta

Bütün parçaya ve ondalık parçaya sahipsiniz. Tüm sayılar bu hassasiyete yuvarlanır (bu kelime vardır - ama nerede olduğunu biliyorsunuz). Örneğin, 3 ondalık basamaklı sabit bir noktaya sahip olabilirsiniz. 15.589+ ondalık kısım için 0.250ekleme 589 + 250 % 1000yapar (ve sonra tüm parçaya taşıma). Bu, mevcut veritabanlarıyla çok iyi çalışıyor. Belirtildiği gibi, yuvarlama var, ancak nerede olduğunu biliyorsunuz ve gerekenden daha kesin olacak şekilde belirtebilirsiniz (sadece 3 ondalık basamağa kadar ölçüyorsunuz, bu yüzden sabitleyin 4).

Kayan sabit nokta

Bir değer ve kesinliği saklayın. 15.589depolanır 15589değeri ve 3süre, hassasiyet için 0.25depolanır 25ve 2. Bu keyfi hassasiyetle başa çıkabilir. Ben Java'nın BigDecimal iç (son zamanlarda bakmadım) kullandığı bu inanıyoruz . Bir noktada, onu bu formattan çıkarmak ve görüntülemek isteyeceksiniz - ve bu yuvarlamayı içerebilir (yine, nerede olduğunu kontrol edersiniz).


Sunum seçimini belirledikten sonra, bunu kullanan mevcut üçüncü taraf kitaplıklarını bulabilir veya kendinizinkini yazabilirsiniz. Kendinizinkini yazarken, birimi test ettiğinizden ve matematiği doğru yaptığınızdan emin olun.


2
Bu iyi bir başlangıç, ama elbette yuvarlama problemini tamamen çözmüyor. Π, e ve √2 gibi mantıksız sayıların kesin bir sayısal temsili yoktur; tam bir temsil istiyorsanız bunları sembolik olarak temsil etmeniz veya yalnızca yuvarlama hatasını en aza indirmek istiyorsanız mümkün olduğunca geç değerlendirmeniz gerekir.
Caleb

Mantıksızlık için @Cebeb, onları herhangi bir yuvarlamanın sorunlara neden olabileceği noktaların ötesinde değerlendirmek gerekir. Örneğin, 22/7 pi değerinin% 0,1'ine doğrudur, 355/113 10 ^ -8 değerine doğrudur. Yalnızca 3 ondalık basamağa kadar sayılarla çalışıyorsanız, 3.141592653 değerine sahip olmak, 3 ondalık basamakta yuvarlama hatalarından kaçınmalıdır.

@MichaelT: Rasyonel sayıların eklenmesi için LCM'yi bulmanız gerekmez ve daha sonra (daha sonra "LSB sıfırlarını" iptal etmek daha hızlıdır ve sadece kesinlikle gerekli olduğunda tamamen basitleştirin). Genel olarak rasyonel sayılar için sadece "pay / payda" veya "pay / payda << üssü" ("bütün parça + pay / payda" değil). Ayrıca "kayan sabit noktanız" bir kayan nokta gösterimidir ve "keyfi boyut kayan nokta" olarak daha iyi tanımlanır ("sabit boyutlu kayan nokta" dan ayırt etmek için).
Brendan

Terminolojinizin bir kısmı biraz havalı - kayan sabit nokta mantıklı değil - sanırım kayan ondalık sayı söylemeye çalışıyorsunuz.
jk.

10

Kayan nokta değerlerinde yuvarlama sorunları varsa ve yuvarlama sorunlarıyla karşılaşmak istemiyorsanız, mantıklı olarak tek işlemin kayan nokta değerlerini kullanmaması mantıklıdır.

Şimdi soru, "kayan nokta değişkenleri olmadan tamsayı olmayan değerleri içeren matematiği nasıl yaparım?" Cevap, keyfi hassas veri türleriyle ilgilidir . Hesaplamalar daha yavaştır, çünkü donanım yerine yazılımda uygulanması gerekir, ancak doğrudur. Hangi dili kullandığınızı söylemediniz, bu yüzden bir paket öneremem, ancak en popüler programlama dilleri için keyfi hassas kütüphaneler var.


Şu anda VC ++ kullanıyorum ... Ama diğer programlama dilleri ile ilgili daha fazla bilgi için teşekkür ederim.
JNL

Kayan nokta değerleri olmasa bile, hala yuvarlak problemlerle karşılaşacaksınız.
Çad

2
@Chad Doğru, ancak amaç yuvarlama sorunlarını ortadan kaldırmak değil (her zaman var olacak, çünkü kullandığınız herhangi bir bazda tam bir temsili olmayan ve sonsuz bellek ve işlem gücünüz olmayan bazı sayılar var) yapmaya çalıştığınız hesaplamada hiçbir etkisi olmadığı noktaya indirin.
Iker

@Iker Haklısın. Siz de soruyu soran kişi, tam olarak hangi hesaplamaları yapmaya çalıştıklarını ve istedikleri hassasiyeti belirtmediniz. Silahı sayı teorisine atmadan önce bu soruyu cevaplaması gerekiyor. Sadece söylemek lot of mathematical calculationsyararlı değil ne de verilen cevaplar. Davanın büyük çoğunluğunda (para birimi ile uğraşmıyorsanız) şamandıra gerçekten yeterli olmalıdır.
Çad

@Chad bu adil bir nokta, OP'den tam olarak ihtiyaç duydukları hassasiyet seviyesinin ne olduğunu söylemek için yeterli veri yok.
Iker

7

Kayan nokta aritmetiği genellikle oldukça hassastır (a için 15 ondalık basamak double) ve oldukça esnektir. Kesinlik rakamlarını önemli ölçüde azaltan matematik yaparken sorunlar ortaya çıkar. İşte bazı örnekler:

  • Çıkarmada iptal: 1234567890.12345 - 1234567890.12300sonuç 0.0045sadece iki ondalık basamak hassasiyetine sahiptir. Bu, aynı büyüklükteki iki sayıyı çıkardığınızda çarpıcı olur.

  • Hassasiyet yutulması: 1234567890.12345 + 0.123456789012345için değerlendirir 1234567890.24691, saniyenin son on basamak işlenen kaybolur.

  • Çarpma İşlemleri: İki 15 basamaklı sayıyı çarparsanız, sonucun saklanması gereken 30 basamak vardır. Ancak bunları saklayamazsınız, bu yüzden son 15 bit kaybolur. Bu, a ile birleştirildiğinde özellikle rahatsız edicidir sqrt()(olduğu gibi sqrt(x*x + y*y): Sonuç sadece 7,5 basamak hassasiyete sahip olacaktır.

Bunlar, bilmeniz gereken ana tuzaklar. Ve bir kez farkında olduktan sonra, matematiğinizi bunlardan kaçınacak şekilde formüle etmeye çalışabilirsiniz. İncelemek için, bir döngüde bir değeri tekrar tekrar artırmanız gerekiyorsa, bunu yapmaktan kaçının:

for(double f = f0; f < f1; f += df) {

Birkaç iterasyondan sonra, daha büyük folan hassasiyetin bir kısmını yutar df. Daha da kötüsü, hatalar toplanacak ve daha küçük dfolanın genel sonuçların daha kötü olmasına yol açabilecek kontraintüratif duruma yol açacaktır. Bunu daha iyi yaz:

for(int i = 0; i < (f1 - f0)/df; i++) {
    double f = f0 + i*df;

Artışları tek bir çarpmada birleştirdiğiniz için, sonuç f15 ondalık basamağa kadar kesin olacaktır.

Bu sadece bir örnektir, başka nedenlerden dolayı hassasiyet kaybını önlemenin başka yolları da vardır. Ancak, zaten dahil edilen değerlerin büyüklüğü hakkında düşünmek ve her adımdan sonra sabit bir sayıya yuvarlayarak, matematiğinizi kalem ve kağıtla yapsaydınız ne olacağını hayal etmek zaten çok yardımcı olur.


2

Sorununuz olmadığından nasıl emin olunur: Kayan nokta aritmetik sorunları hakkında bilgi edinin veya bunu yapan birini işe alın veya sağduyunuzu kullanın.

İlk sorun hassasiyettir. Birçok dilde "şamandıra" ve "çift" ("çift kesinlik" için çift ayakta) vardır ve birçok durumda "şamandıra" size yaklaşık 7 basamak hassasiyeti verirken, çift size 15 verir. hassasiyetin bir sorun olabileceği bir durumda, 15 basamak 7 basamaktan çok daha iyi. Pek çok sorunlu durumda, "çift" kullanmak, onunla birlikte kaçacağınız anlamına gelir ve "kayan" ise yapmazsınız demektir. Diyelim ki bir şirketin piyasa değeri 700 milyar dolar. Şamandıra içinde bunu temsil eder ve en düşük bit 65536 $ 'dır. Çift kullanarak temsil edin ve en düşük bit yaklaşık 0.012 senttir. Yani gerçekten ne yaptığınızı gerçekten bilmediğiniz sürece, şamandıra değil, çift kullanırsınız.

İkinci sorun daha çok prensip meselesidir. Aynı sonucu vermesi gereken iki farklı hesaplama yaparsanız, genellikle yuvarlama hataları nedeniyle yapmazlar. Eşit olması gereken iki sonuç "neredeyse eşit" olacaktır. İki sonuç birbirine yakınsa, gerçek değerler eşit olabilir. Ya da olmayabilirler. Bunu aklınızda bulundurmanız ve "x kesinlikle y'den büyük" veya "x kesinlikle y'den küçük" veya "x ve y eşit olabilir" işlevlerini yazıp kullanmanız gerekir.

Yuvarlama kullanırsanız, örneğin "x'i en yakın tamsayıya yuvarla" kullanıyorsanız, bu sorun çok daha da kötüleşir. 120 * 0,05 ile çarparsanız, sonuç 6 olmalıdır, ancak elde ettiğiniz "6'ya çok yakın bir sayıdır". Daha sonra "en yakın tamsayıya yuvarlansanız", bu "6'ya çok yakın sayı", "6'dan biraz daha az" olabilir ve 5'e yuvarlanabilir. Ve ne kadar hassasiyetiniz olduğuna dikkat edin. 6'dan az olduğu sürece, sonucunuzun 6'ya ne kadar yakın olduğu önemli değildir .

Üçüncüsü, bazı problemler zor . Bu, hızlı ve kolay bir kural olmadığı anlamına gelir. Derleyiciniz "long double" özelliğini daha hassas bir şekilde destekliyorsa "long double" kullanabilirsiniz ve fark yaratıp yaratmadığını görebilirsiniz. Fark etmezse, ya Tamam, ya da gerçek bir zorluğun var. Eğer (12. ondalık bir değişiklik gibi) beklediğiniz tür bir fark yaparsa, o zaman büyük olasılıkla iyi. Sonuçlarınızı gerçekten değiştirirse, bir sorununuz var demektir. Yardım isteyin.


1
Kayan nokta matematiği hakkında "sağduyu" diye bir şey yoktur.
whatsisname

Bununla ilgili daha fazla bilgi edinin.
gnasher729

0

Çoğu insan iki katına çıktıklarında BigDecimal'i çığlık attığında hata yapar, aslında sorunu başka bir yere taşımışlardır. Çift, İşaret biti: 1 bit, Üs genişliği: 11 bit verir. Önemli hassasiyet: 53 bit (52 açıkça saklanır). Çifte doğası nedeniyle, tüm interger ne kadar büyük olursa göreceli doğruluğu kaybedersiniz. Burada kullandığımız göreceli doğruluğu hesaplamak için körüktür.

Hesaplamada çifte göreceli doğruluk aşağıdaki foluma kullanıyoruz 2 ^ E <= abs (X) <2 ^ (E + 1)

epsilon = 2 ^ (E-10)% 16 bit kayan nokta için (yarı duyarlık)

 Accuracy Power | Accuracy -/+| Maximum Power | Max Interger Value
 2^-1           | 0.5         | 2^51          | 2.2518E+15
 2^-5           | 0.03125     | 2^47          | 1.40737E+14
 2^-10          | 0.000976563 | 2^42          | 4.39805E+12
 2^-15          | 3.05176E-05 | 2^37          | 1.37439E+11
 2^-20          | 9.53674E-07 | 2^32          | 4294967296
 2^-25          | 2.98023E-08 | 2^27          | 134217728
 2^-30          | 9.31323E-10 | 2^22          | 4194304
 2^-35          | 2.91038E-11 | 2^17          | 131072
 2^-40          | 9.09495E-13 | 2^12          | 4096
 2^-45          | 2.84217E-14 | 2^7           | 128
 2^-50          | 8.88178E-16 | 2^2           | 4

Başka bir deyişle +/- 0,5 (veya 2 ^ -1) Doğruluk istiyorsanız, sayının olabileceği maksimum boyut 2 ^ 52'dir. Bundan büyük ve kayan nokta sayıları arasındaki mesafe 0,5'ten büyüktür.

+/- 0.0005 (yaklaşık 2 ^ -11) doğruluk istiyorsanız, sayının olabileceği maksimum boyut 2 ^ 42'dir. Bundan büyük ve kayan nokta sayıları arasındaki mesafe 0.0005'ten büyüktür.

Bundan daha iyi bir cevap veremiyorum. Kullanıcı, gerekli hesaplamayı ve birim değerini (Metre, Feet, İnç, mm, cm) yaparken ne kadar hassasiyet istediğini bulmalıdır. Vakaların büyük çoğunluğu için, simüle etmeyi amaçladığınız dünyanın ölçeğine bağlı olarak şamandıra basit simülasyonlar için yeterli olacaktır.

Söylenecek bir şey olsa da, sadece 100 metreye 100 metrelik bir dünyayı simüle etmeyi hedefliyorsanız, 2 ^ -45 civarında doğruluk sırasına sahip bir yere sahip olacaksınız. Bu, cpu'nun içindeki modern FPU'nun yerel tip boyutu dışında nasıl hesaplamalar yapacağına bile girmez ve sadece hesaplama tamamlandıktan sonra (FPU yuvarlama moduna bağlı olarak) yerel tip boyutuna yuvarlanır.

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.