Kayan nokta yuvarlama hatalarına neden olan nedir?


62

Kayan nokta aritmetiğinin hassas problemleri olduğunu biliyorum. Genelde sayının sabit bir ondalık göstergesine geçerek veya sadece hatayı ihmal ederek bunların üstesinden gelirim.

Ancak, bu yanlışlığın nedenleri nelerdir bilmiyorum. Şamandıra numaraları ile neden bu kadar çok sayıda yuvarlama sorunu var?


28
Kesin olarak, çoğu insanın endişelendiği yuvarlamadan kaynaklanan hata değil - ikili kayan nokta yuvarlama işlemlerinin sezgisel olmayan şekilde davranması gerçeğidir. Ondalık gösterime geçmek, yuvarlamanın daha sezgisel bir şekilde davranmasını sağlayabilir, ancak buna karşılık olarak göreceli hatayı neredeyse her zaman artıracaksınız (veya telafi etmek için depolama alanını artırmak zorunda kalacaksınız ).
Daniel Pryden

12
En yaygın kafa karışıklıklarını
gidermeye

@DanielPryden'in ne anlama geldiğini düşünüyorum: "[Sabit nokta] gösterime geçmek, yuvarlamanın daha sezgisel bir şekilde çalışmasını sağlayabilir ..." . Ne neden o sabit veya kayan nokta sayıları olsun, sorunları yuvarlama ya sonlu kelime genişliğidir. kayan nokta ile yuvarlama hatasının büyüklüğü normalde yuvarlanan sayının büyüklüğü ile kabaca orantılı kalır. (Eğer gerçekten küçük ve sayıları "denormalized" gidince hariç.)
Robert bristow-johnson

@ robert: Bahsettiğim tam olarak bu değildi. Kayan nokta ile karşılaşan çoğu insan "hata", kayan nokta ile ilgili bir şey değildir, temeldir. IEEE-754 yüzer ve çiftler, taban 2'de bir üs kullanır, bu da kesirli sayıların, 10 (1/1/1) negatif güç yerine negatif iktidarlara (1/2, 1/16, 1/1024 vb.) Yuvarlandığı anlamına gelir. 10, 1/1000, vb.) Bu, 0.1 yuvarlamadan 0.1000001'e kadar yuvarlanma ve benzeri hususlar gibi sezgisel olmayan sonuçlara yol açar.
Daniel Pryden

Kayan nokta sayılarını taban 10'da yapabilirsiniz - .NET'in decimaltürü bu şekilde çalışır. Diğer taraftan, sabit nokta farklıdır. Menziliniz sınırlı olduğu sürece, sabit nokta iyi bir cevaptır. Ancak, kısıtlayıcı aralık, sabit noktayı birçok matematiksel uygulama için uygun kılmaz ve sabit nokta sayılarının uygulanması, donanımda sonuç olarak iyi bir şekilde optimize edilmez.
Daniel Pryden

Yanıtlar:


82

Bunun nedeni, bazı fraksiyonların yuvarlama olmadan ifade edilmek için çok büyük (veya hatta sonsuz) miktarda bir yere ihtiyaç duymasıdır. Bu, ikili ya da başka herhangi bir sayıdaki ondalık gösterim için geçerlidir. Hesaplamalarınız için kullanılacak ondalık basamak miktarını sınırlarsanız (ve kesir gösteriminde hesaplama yapmaktan kaçınırsanız), basit bir ifadeyi bile 1/3 + 1/3 olarak yuvarlamanız gerekir. Sonuç olarak 2/3 yazmak yerine, 0.3333 + 0.33333 = 0.66666 yazmanız gerekir, ki bu 2/3 ile aynı değildir.

Bir bilgisayar durumunda, hane sayısı, hafızasının ve CPU kayıtlarının teknik niteliği ile sınırlıdır. Dahili olarak kullanılan ikili gösterim, daha fazla zorluk ekler. Bilgisayarlar normal olarak kesir gösteriminde sayıları ifade edemezler, ancak bazı programlama dilleri, bu sorunların belirli bir dereceye kadar önlenmesini sağlayan bu yeteneği ekler.

Her Bilgisayar Bilim İnsanının Kayan Nokta Aritmetiği Hakkında Bilmeleri Gerekenler


12
Noktaya bak. Ancak, ondalık olarak sonlanan bazı sayıların ikili olarak sonlanmadığını da not edeceğim. Özelde, 0.1, ikili olarak tekrarlayan bir sayıdır ve bu nedenle hiçbir kayan nokta ikili sayısı, tam olarak 0.1'i temsil edemez.
Jack Aidley

4
Kayan noktalar, yalnızca ondalık basamak için yararlı değildir. 32 bit tam sayılar yalnızca 4 milyar sayar, ancak 32 bitlik bir yüzgeç neredeyse sonsuz büyüklükte olabilir.
Abhi Beckert,

7
Özellikle, sonlu ondalık sayı olarak ifade edebileceğimiz fraksiyonlar, paydaşların asal çarpanlarını yalnızca 2 ve 5 içerenlerdir (örneğin, 3/10 ve 7/25 ifade edebiliriz, ancak 11/18 değil). İkiliğe geçtiğimizde, 5 faktörünü kaybederiz, böylece yalnızca dyadik rasyoneller (örneğin 1/4, 3/128) tam olarak ifade edilebilir.
David Zhang

70

Öncelikle, yuvarlama hataları , tüm gerçek sayıların sonsuzluğunun muhtemelen bir bilgisayarın sonlu hafızası tarafından temsil edilememesi gerçeğinden kaynaklanmaktadır, tek bir kayan nokta değişkeni gibi küçük bir hafıza dilimi bile olsa , saklanan birçok sayı sadece yaklaşık değerlerdir. temsil etmeleri gereken sayı.

Yaklaşım olmayan yalnızca sınırlı sayıda değer olduğundan ve yaklaşık değerle başka sayı arasındaki herhangi bir işlem yaklaşık değere neden olduğundan , yuvarlama hataları neredeyse kaçınılmazdır .

Önemli olan etmektir onlar bir soruna neden olabileceği durumlarda fark ve riskleri azaltmak için adımlar atması .


Ek olarak David Goldberg 'in temel Kayan Nokta Aritmetiği Hakkında Her Bilgisayar Mühendisi Bilmeniz Gerekenler (kendi bir ek olarak Güneş / Oracle tarafından yeniden yayınlanan Sayısal Hesaplama Rehberi tarafından bahsedildi,) thorsten , ACCU dergi Aşırı yük mükemmel koştu Richard Harris'in Floating Point Blues hakkındaki makaleleri .

Dizi ile başladı

Sayısal hesaplamanın birçok tuzağı vardır. Richard Harris gümüş bir mermi aramaya başlar.

Sayısal hata ejderhası genellikle onun uykusundan yönlendirilmez, ancak dikkatsizce yaklaşırsa, bazen programcının hesaplamaları üzerine zaman zaman feci zarar verir.

Öyle ki, bazı programcılar, IEEE 754 kayan nokta aritmetiği ormanlarında kendini değiştirmiş, arkadaşlarına bu adil topraklarda seyahat etmeme konusunda tavsiyede bulunmuşlardır.

Bu yazı dizisinde, sayısal hesaplama dünyasını keşfedeceğiz, kayan nokta aritmetiğini, bunun için daha güvenli bir alternatif olarak önerilmiş olan bazı tekniklerle karşılaştırmalı olarak inceleyeceğiz. Ejderhanın topraklarının gerçekten çok uzaklara ulaştığını ve yıkıcı dikkatinden korkarsak, genel olarak dikkatli bir şekilde yürümemiz gerektiğini öğreneceğiz.

Richard, rasyonel, irrasyonel, cebirsel ve aşkın olan gerçek sayıların taksonomisini açıklayarak başlar. Daha sonra iptal hatası ve uygulama sırası sorunlarına devam etmeden önce IEEE754 temsilini açıklamaya devam ediyor.

Bundan daha derin bir okuma yapmazsanız, kayan nokta sayılarıyla ilgili problemlerde mükemmel bir topraklamaya sahip olacaksınız.

Ancak daha fazla bilmek istiyorsanız, o devam ediyor

Daha sonra Calculus Blues'unuzu tedavi etmenize yardımcı olmaya çalışıyor

ve son fakat en az değil, var

Bütün yazı serileri araştırılmaya değer ve toplamda 66 sayfada, yine de Goldberg gazetesinin 77 sayfasından daha küçük .

Bu seri aynı zemini kapsıyor olsa da, Goldberg'in gazetesinden daha erişilebilir buldum . Ayrıca, Richards makalelerinin önceki bölümlerini okuduktan sonra makalenin daha karmaşık kısımlarını anlamayı daha kolay buldum ve bu ilk makalelerin ardından Richard, Goldberg makalesinde değinilmeyen birçok ilginç alana daldı.


As Böyle Buyurdu ak yorum olarak bahsedilen:

Bu makalelerin yazarı olarak, thusspakeak.com/ak/2013/06 ile başlayan www.thusspakeak.com blogumda etkileşimli sürümleri oluşturduğumu belirtmek isterim .


1
Bu makalelerin yazarı olarak, thusspakeak.com/ak/2013/06 ile başlayan www.thusspakeak.com blogumda etkileşimli sürümleri oluşturduğumu belirtmek isterim .
Böylece

Teşekkürler @ thusspakea.k. Cevabımı notu ekledim ve bu etkileşimli öğeler çok iyi çalışıyor.
Mark Booth,

12

Eh, thorsten kesin vardır linki . Ekleyeceğim:

Herhangi bir temsil biçiminin, bazı sayılar için yuvarlama hataları olacaktır. IEEE kayan noktasında veya ondalık olarak 1/3 ifade etmeye çalışın. Hiçbiri doğru şekilde yapamaz. Bu, sorunuzu yanıtlamanın ötesine geçiyor, ancak bu kural kuralını başarıyla kullandım:

  • Kullanıcı tarafından girilen değerleri ondalık basamakta saklayın (neredeyse kesinlikle bir ondalık gösterime girdiklerinden - çok az kullanıcı ikili veya altıgen kullanır). Bu şekilde, daima kullanıcı tarafından girilen temsillere sahip olursunuz.
  • Kullanıcı tarafından girilen kesirleri saklamanız gerekiyorsa, pay ve paydayı (ayrıca ondalık) saklayın
  • Aynı miktar için (Celsius / Fahrenheit gibi) birden fazla ölçü birimine sahip bir sisteminiz varsa ve kullanıcı her ikisini de girebiliyorsa, girdikleri değeri ve girdikleri birimleri saklayın. Dönüştürmeye ve kaydetmeye çalışmayın Tek bir gösterim, hassasiyet / doğruluk kaybı olmadan yapamazsanız. Tüm hesaplamalarda kayıtlı değeri ve birimleri kullanın .
  • Makine tarafından üretilen değerleri IEEE kayan noktalarında saklayın (bu, A / D dönüştürücülü bir analog sensör veya bir hesaplamanın çevrilmemiş sonucu gibi, elektronik bir ölçüm cihazı tarafından üretilen sayılar olabilir). Seri bağlantı üzerinden bir sensör okuyorsanız bunun geçerli olmadığını ve değeri zaten ondalık biçimde (örneğin 18.2 C) verdiğini unutmayın.
  • Kullanıcı tarafından görüntülenebilir toplamları vb. Ondalık basamakta saklayın (bir banka hesap bakiyesi gibi). Uygun şekilde yuvarlayın, ancak gelecekteki tüm hesaplamalar için bu değeri kesin değer olarak kullanın.

Eklerim: ARPREC veya decNumber gibi isteğe bağlı bir matematik paketini kullanmayı düşünün.
Blrfl

Ondalık (ikili yerine) kesirlerin pay ve paydaları gibi tamsayı değerleri için çok faydası yoktur. Her ikisi de tam sayı değerlerini saklayabilir ve ikili dosya daha verimlidir. Giriş ve çıkış için ileri geri dönüştürme maliyeti vardır, ancak bu fiziksel olarak G / Ç'yi gerçekleştirmenin maliyeti ile karıştırılmalıdır.
Keith Thompson

10

Şimdiye kadar bahsedilmemiş gibi görünen şey, kararsız bir algoritma ve koşulsuz bir problemin kavramlarıdır . İlkine hitap edeceğim, çünkü acemi sayısalcılar için daha sık görülen bir sorun.

(Karşılıklı) altın oranın güçlerinin hesaplanmasını düşünün φ=0.61803…; Bunu yapmanın bir yolu, özyinelemeyi ve φ^n=φ^(n-2)-φ^(n-1)ile başlayan formülünü kullanmaktır . Bu özyinelemeyi en sevdiğiniz bilgisayar ortamınızda yaparsanız ve sonuçları doğru bir şekilde değerlendirilmiş güçlerle karşılaştırırsanız, önemli rakamların yavaşça erozyonunu görürsünüz. Mathematica’da örneğin olanlar :φ^0=1φ^1=φ

ph = N[1/GoldenRatio];  
Nest[Append[#1, #1[[-2]] - #1[[-1]]] & , {1, ph}, 50] - ph^Range[0, 51]  
{0., 0., 1.1102230246251565*^-16, -5.551115123125783*^-17, 2.220446049250313*^-16, 
-2.3592239273284576*^-16, 4.85722573273506*^-16, -7.147060721024445*^-16, 
1.2073675392798577*^-15, -1.916869440954372*^-15, 3.1259717037102064*^-15, 
-5.0411064211886014*^-15, 8.16837916750579*^-15, -1.3209051907825398*^-14, 
2.1377864756200182*^-14, -3.458669982359108*^-14, 5.596472721011714*^-14, 
-9.055131861349097*^-14, 1.465160458236081*^-13, -2.370673237795176*^-13, 
3.835834102607072*^-13, -6.206507137114341*^-13, 1.004234127360273*^-12, 
-1.6248848342954435*^-12, 2.6291189633497825*^-12, -4.254003796798193*^-12, 
6.883122762265558*^-12, -1.1137126558640235*^-11, 1.8020249321541067*^-11, 
-2.9157375879969544*^-11, 4.717762520172237*^-11, -7.633500108148015*^-11, 
1.23512626283229*^-10, -1.9984762736468268*^-10, 3.233602536479646*^-10, 
-5.232078810126407*^-10, 8.465681346606119*^-10, -1.3697760156732426*^-9, 
2.216344150333856*^-9, -3.5861201660070964*^-9, 5.802464316340953*^-9, 
-9.388584482348049*^-9, 1.5191048798689004*^-8, -2.457963328103705*^-8, 
3.9770682079726053*^-8, -6.43503153607631*^-8, 1.0412099744048916*^-7, 
-1.6847131280125227*^-7, 2.725923102417414*^-7, -4.4106362304299367*^-7, 
7.136559332847351*^-7, -1.1547195563277288*^-6}

Bunun için φ^41öngörülen sonuç yanlış işarete sahip ve daha önce hesaplanan ve hesaplanan gerçek değerler φ^39ortak rakamları paylaşmıyor ( 3.484899258054952* ^ - 9 for the computed version against the true value7.071019424062048 *^-9). Bu nedenle algoritma kararsızdır ve bu özyineleme formülünü tam olmayan aritmetik olarak kullanmamalısınız. Bu, özyinelemenin doğasında var olan doğası gereğidir: bu özyinelemede "çürüyen" ve "büyüyen" bir çözüm vardır ve alternatif bir "büyüyen" çözüm dilendiğinde "çürüyen" çözümü ileriye doğru çözerek hesaplamaya çalışmaktır. sayısal keder için. Dolayısıyla, sayısal algoritmalarının kararlı olmasını sağlamalıdır.

Şimdi, koşulsuz bir sorun kavramı üzerine : sayısal olarak bir şeyi yapmanın kararlı bir yolu olsa da, problemin algoritmanız tarafından çözülememesi çok iyi olabilir. Bu, çözüm yönteminin değil sorunun kendisinin hatasıdır. Nümerikteki kanonik örnek, "Hilbert matrisi" olarak adlandırılan doğrusal denklemlerin çözümüdır:

Hilbert matrisi

Matris bir kanonik örneğidir kötü koşullanmış bir yanlış çözüm döndürebilir büyük Hilbert matris ile bir sistemi çözmeye çalışmak: matriksi.

İşte bir Mathematica gösterisi: Kesin aritmetiğin sonuçlarını karşılaştır

Table[LinearSolve[HilbertMatrix[n], HilbertMatrix[n].ConstantArray[1, n]], {n, 2, 12}]
{{1, 1}, {1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 
  1}, {1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1,
   1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 
  1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}

ve tam olmayan aritmetik

Table[LinearSolve[N[HilbertMatrix[n]], N[HilbertMatrix[n].ConstantArray[1, n]]], {n, 2, 12}]
{{1., 1.}, {1., 1., 1.}, {1., 1., 1., 1.}, {1., 1., 1., 1., 1.},  
  {1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1.}, 
  {1., 1., 1., 1., 1., 1., 1., 1.}, {1., 1., 1., 1., 1., 1., 1., 1., 1.},  
  {1., 1., 1., 0.99997, 1.00014, 0.999618, 1.00062, 0.9994, 1.00031, 
  0.999931}, {1., 1., 0.999995, 1.00006, 0.999658, 1.00122, 0.997327, 
  1.00367, 0.996932, 1.00143, 0.999717}, {1., 1., 0.999986, 1.00022, 
  0.998241, 1.00831, 0.975462, 1.0466, 0.94311, 1.04312, 0.981529, 
  1.00342}}

( Mathematica'da denediyseniz , kötü görünen şartlara dair bir kaç hata mesajı bulunduğunu not edersiniz.)

Her iki durumda da, kesinliği arttırmak bir tedavi değildir; yalnızca rakamların kaçınılmaz aşınmasını geciktirir.

Bununla karşılaşabileceğin şey bu. Çözümler zor olabilir: birincisi, çizim tahtasına geri dönersiniz ya da dergilerden / kitaplardan geçtiniz ya da bir başkasının sizden daha iyi bir çözüm getirip getirmediğini bulmak için ne olursa olsun; ikincisi, sorununuzu daha izlenebilir bir şeye bırakmanız ya da bırakmanız.


Seni Dianne O'Leary'den bir teklifle bırakacağım:

Yaşam bize bazı rahatsız edici sorunları atlatabilir, ancak dengesiz bir algoritmaya razı olmak için iyi bir neden yoktur.


9

çünkü taban 10 ondalık sayıları taban 2'de ifade edilemez

veya başka bir deyişle, 1/10 payda (2 kayan nokta sayıları esas olandır) 2 gücüne sahip bir fraksiyona dönüştürülemez.


11
Tam olarak doğru değil: 0,5 ve 0,25, 2. temelde ifade edilebilir. Bence "10 temel sayının tamamı değil" anlamına gelir.
Scott Whitlock

3
Daha doğru. Kesirli sayıların tümü bir kayan nokta gösterimi kullanılarak tam olarak gösterilemez (yani, hem taban 2 hem de taban 10 bu kesin soruna sahiptir). 9*3.3333333Ondalık yapmayı deneyin ve buna uyum sağlayın9*3 1/3
Martin York

1
Bu, kayan nokta karışıklığının en yaygın kaynağıdır. .1 + .1 != .2çünkü kayan noktalı ikili kodlama kullanılır, ondalık değil.
Sean McMillan

@SeanMcMillan: Ve 1.0/3.0*3.0 != 1.0, kayan nokta ikili kodlama kullanıldığından, triner değil.
Keith Thompson

8

Matematikte, sonsuz sayıda rasyonel sayı vardır. 32 bit değişken yalnızca 2 32 farklı değere ve 64 bit değişken yalnızca 2 64 değere sahip olabilir. Bu nedenle, kesin bir temsili olmayan sonsuz sayıda rasyonel sayı vardır.

1/3 veya 1/100 olarak mükemmel şekilde temsil etmemize izin verecek programlar yapabiliriz. Birçok pratik amaç için bunun çok kullanışlı olmadığı ortaya çıktı. Bunun büyük bir istisnası var: Finans alanında, ondalık kesirler sıklıkla ortaya çıkıyor. Bunun nedeni çoğunlukla finans aslında fiziksel bir faaliyet değil, insan faaliyetleridir.

Bu nedenle, genellikle ikili kayan nokta kullanmayı seçer ve ikili biçimde temsil edilemeyen herhangi bir değeri yuvarlarız. Ancak finansta bazen ondalık kayan nokta seçeriz ve değerleri en yakın ondalık değere yuvarlarız.


2
Daha da kötüsü, sınırsız (sayılabilir şekilde sonsuz) miktarda bellek kişinin tüm rasyonelleri temsil etmesini sağlarken, gerçekleri temsil etmek için yeterli olmaz. Daha da kötüsü, gerçek sayıların neredeyse tamamı hesaplanabilir sayı değildir. Sınırlı miktarda bellekle yapabileceğimiz en iyi şey, gerçeklerin sınırlı bir aralık alt kümesine yaklaşmaktır.
David Hammen

4
@Kevin: Gerçeklerin minik bir altkümesi (sıfır ölçülü bir altkümesi) olan hesaplanabilir sayılardan bahsediyorsunuz.
David Hammen

1
En temel açıklama için +1: Sınırlı sayıda bit olan sonsuz sayıda sayıyı göstermeye çalışıyorsunuz.
Raku

1
@DavidHammen: Hesaplanabilir sayılar gerçeklerin minik bir alt kümesidir (sıfır ölçüsünün) - ancak bir programda birlikte çalışacağınız her sayı, tanım gereği hesaplanabilir.
Keith Thompson

3
@Giorgio: Eğer doğru temsilini seçerseniz, 2 kare kökü olan dize olarak, örneğin, Temsil "√2". (Eski HP-48 hesap makinem tam olarak bunu başardı ve bu değerin karesi tam olarak sonuçlandı 2.0.) Herhangi bir sonlu gösterim için sadece temsil edilebilir gerçek sayıların sayılabilir bir sonsuzluğu vardır - ancak hiçbir hesaplama, olmayan bir sayı veremez. prensip olarak temsil edilebilir. Uygulamada, ikili kayan nokta, sembolik gösterimlere göre yanan hız ve küçük depolamanın faydası ile temsil edilebilir sayı kümesini büyük ölçüde sınırlar.
Keith Thompson

-2

Bence kayan nokta sayıları ile gerçekten bariz "yuvarlama sorunu" Ben hareketli ortalama filtreleri ile olduğunu:

$$ \ begin {align} y [n] & = \ frac {1} {N} \ sum \ sınırlar_ {i = 0} ^ {N-1} x [ni] \ & = y [n-1] + \ frac {1} {N} (x [n] - x [nN]) \ \ end {align} $$

Gürültü birikimi olmadan bu işi yapmak için, mevcut örneklere eklediğiniz $ x [n] $ 'ın aynen $ x [nN] $ ile tamamen aynı olduğundan emin olmak istersiniz. geleceği. eğer değilse, o zaman farklı olan, gecikme hattınıza sıkışan ve asla çıkamayacak olan küçük bir boktur. Bunun nedeni, bu hareketli ortalama filtrenin aslında $ z = 1 $ seviyesinde marjinal olarak sabit bir direği ve içinde onu iptal eden sıfır olan bir IIR ile yapılmış olmasıdır. ancak, bu bir bütünleştiricidir ve bütünüyle bütünleşmiş ve tamamen kaldırılmamış herhangi bir saçmalık, daha önce bütünleştirici toplamında var olacaktır. sabit nokta, kayan nokta sayılarının yaptığı gibi aynı problemin olmadığı yerdir.


hey, $ LaTeX $ matematik biçimlendirme prog.SE forumunda çalışmaz mı ??? eğer yapmazsa bu gerçekten topal.
robert bristow-johnson

1
Bkz meta.SO bu ve bağlantılı sorular
AakashM
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.