Kayan nokta hesaplamalarında hassasiyet ve doğruluk

Not

Office 365 ProPlus, Microsoft 365 Kurumsal Uygulamaları olarak yeniden adlandırılıyor. Bu değişiklik hakkında daha fazla bilgi için, bu blog yazısını okuyun.

Orijinal KB numarası:   125056

Özet

Kayan nokta hesaplamalarında hassasiyet, yuvarlama ve doğruluk programcı için şaşırtıcı sonuçlar oluşturmak için çalışabileceği birçok durum vardır. Dört genel kurala uymalılar:

  1. Hem tek hem de çift duyarlık içeren bir hesaplamada, sonuç genellikle tek bir hassasiyetten daha doğru olmayacaktır. Çift hassasiyet gerekiyorsa, sabitler de dahil olmak üzere hesaplamadaki tüm terimlerin çift hassasiyetle belirtildiğinden emin olun.

  2. Bilgisayarda basit bir sayısal değerin doğru bir şekilde temsil edildiğini asla düşünmeyin. Kayan nokta değerlerinin çoğu sonlu ikili değer olarak tam olarak temsil edilemez. Örneğin, .1 .0001100110011... ikili (sonsuza kadar tekrarlanır), bu nedenle tüm bilgisayarları içeren ikili aritmetik kullanarak bir bilgisayarda tam doğrulukla temsil edilemez.

  3. Sonucun son ondalık basamak için doğru olduğunu asla varsaymayın. "Doğru" yanıt ile herhangi bir kayan nokta işleme biriminin sonlu hassasiyeti ile hesaplanabilenler arasında her zaman küçük farklar vardır.

  4. Eşit olup olmadıklarını görmek için asla iki kayan nokta değerini karşılaştırmayın. Bu kural 3 için bir corollary olduğunu. Hemen hemen her zaman "olmalıdır" eşit olması sayılar arasında küçük farklılıklar olacak. Bunun yerine, sayıların neredeyse eşit olup olmadığını her zaman kontrol edin. Başka bir deyişle, aralarındaki farkın küçük mü yoksa önemsiz mi olduğunu kontrol edin.

Daha Fazla Bilgi

Genel olarak, yukarıda açıklanan kurallar C, C++ve assembler dahil olmak üzere tüm diller için geçerlidir. Aşağıdaki örnekler FORTRAN PowerStation kullanarak bazı kuralları göstermektedir. Tüm örnekler FORTRAN PowerStation 32 kullanılarak, C ile yazılan sonuncusu hariç hiçbir seçenek olmadan derlenmiştir.

Örnek 1

İlk örnek iki şeyi gösterir:

  • FORTRAN sabitlerinin varsayılan olarak tek bir hassasiyet olduğunu (C sabitleri varsayılan olarak çift duyarlıktadır).
  • Tek bir kesinlik terimleri içeren hesaplamalar, tüm terimlerin tek kesinlikte olduğu hesaplamalardan çok daha doğru değildir.

1.1 (tek bir duyarlık sabiti) ile başharfe basıldıktan sonra, y tek bir duyarlık değişkeni kadar yanlıştır.

x = 1.100000000000000  y = 1.100000023841858

Tek bir duyarlık değerinin doğru bir çift duyarlık değeriyle çarpılması nın sonucu, iki tek duyarlık değeri çarpmak kadar kötüdür. Her iki hesaplamada da iki çift duyarlıklı değeri çarpmak kadar binlerce hata vardır.

true = 1.320000000000000 (multiplying 2 double precision values)
y    = 1.320000052452087 (multiplying a double and a single)
z    = 1.320000081062318 (multiplying 2 single precision values)

Örnek kod

C Compile options: none

       real*8 x,y,z
       x = 1.1D0
       y = 1.1
       print *, 'x =',x, 'y =', y
       y = 1.2 * x
       z = 1.2 * 1.1
       print *, x, y, z
       end

Örnek 2

Örnek 2, kuadratik denklemi kullanır. Çift duyarlıklı hesaplamaların bile mükemmel olmadığını ve küçük hataların köklü sonuçlar doğurup elde edilemeyeceğinin bağlı olmadan önce hesaplamanın sonucunun sınanması gerektiğini gösterir. Örnek 2'deki kare kök işlevine giriş yalnızca biraz negatiftir, ancak yine de geçersizdir. Çift duyarlıklı hesaplamalar hafif hatalar yoksa, sonuç olacaktır:

Root =   -1.1500000000

Bunun yerine, aşağıdaki hatayı oluşturur:

çalışma zamanı hatası M6201: MATEMATİk

  • sqrt: DOMAIN hatası

Örnek kod

C Compile options: none

       real*8 a,b,c,x,y
       a=1.0D0
       b=2.3D0
       c=1.322D0
       x = b**2
       y = 4*a*c
       print *,x,y,x-y
       print "(' Root =',F16.10)",(-b+dsqrt(x-y))/(2*a)
       end

Örnek 3

Örnek 3, optimizasyon açık olmasa bile meydana gelen optimizasyonlar nedeniyle, değerlerin geçici olarak beklenenden daha yüksek bir hassasiyeti koruyabileceğini ve eşitlik için iki kayan nokta değerini test etmek akıllıca olmadığını göstermektedir.

Bu örnekte, iki değer hem eşit hem de eşit değildir. İlk IF'de, Z değeri hala yardımcı işlemcinin yığınındadır ve Y ile aynı duyarlılığa sahiptir. Bu nedenle X Y'ye eşit değildir ve ilk ileti yazdırılır. İkinci IF sırasında, Z bellekten yüklenmesi gerekiyordu ve bu nedenle X ile aynı hassasiyet ve değere sahipti ve ikinci ileti de yazdırılır.

Örnek kod

C Compile options: none

       real*8 y
       y=27.1024D0
       x=27.1024
       z=y
       if (x.ne.z) then
         print *,'X does not equal Z'
       end if
       if (x.eq.z) then
         print *,'X equals Z'
       end if
       end

Örnek 4

Örnek kod 4'ün ilk bölümü, 1,0'a yakın iki sayı arasındaki mümkün olan en küçük farkı hesaplar. Bunu, 1.0'ın ikili gösterimine tek bir bit ekleyerek yapar.

x   = 1.00000000000000000  (one bit more than 1.0)
y   = 1.00000000000000000  (exactly 1.0)
x-y =  .00000000000000022  (smallest possible difference)

FORTRAN'ın bazı sürümleri, sayıları görüntülerken yuvarlar, böylece sayısal yanlışlık o kadar da belirgin değildir. Bu nedenle x ve y görüntülendiğinde aynı görünür.

Örnek kod 4'ün ikinci bölümü, 10.0'a yakın iki sayı arasındaki mümkün olan en küçük farkı hesaplar. Yine, 10.0 ikili gösterimi için tek bir bit ekleyerek bunu yapar. 10'a yakın sayılar arasındaki farkın 1'e yakın olan farktan daha büyük olduğuna dikkat edin. Bu, bir sayının mutlak değeri ne kadar büyükse, belirli sayıda bitte o kadar az kesin olarak depolanabileceği genel prensibini gösterir.

x   = 10.00000000000000000  (one bit more than 10.0)
y   = 10.00000000000000000  (exactly 10.0)
x-y =   .00000000000000178

Bu sayıların ikili gösterimi de sadece 1 bit farklı olduğunu göstermek için görüntülenir.

x = 4024000000000001 Hex
y = 4024000000000000 Hex

Örnek kod 4'ün son bölümü, basit yinelemeli ondalık değerlerin genellikle ikili olarak yalnızca yinelenen bir kesirle gösterilebileni gösterir. Bu durumda x=1.05, tekrarlayıcı bir faktör CCCCCCCC gerektirir .... (Hex) mantissa. FORTRAN'da, mümkün olan en yüksek doğruluğu sağlamak için son basamak "C" "D"ye yuvarlanır:

x = 3FF0CCCCCCCCCCCD (Hex representation of 1.05D0)

Yuvarlama sonra bile, sonuç mükemmel doğru değildir. En az anlamlı basamaktan sonra, ilk basamağı kaldırarak görebileceğimiz bazı hatalar vardır.

x-1 = .05000000000000004

Örnek kod

C Compile options: none

       IMPLICIT real*8 (A-Z)
       integer*4 i(2)
       real*8 x,y
       equivalence (i(1),x)

       x=1.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *

       x=10.
       y=x
       i(1)=i(1)+1
       print "(1x,'x  =',F20.17,'  y=',f20.17)", x,y
       print "(1x,'x-y=',F20.17)", x-y
       print *
       print "(1x,'x  =',Z16,' Hex  y=',Z16,' Hex')", x,y
       print *

       x=1.05D0
       print "(1x,'x  =',F20.17)", x
       print "(1x,'x  =',Z16,' Hex')", x
       x=x-1
       print "(1x,'x-1=',F20.17)", x
       print *
       end

Örnek 5

C'de kayan sabitler varsayılan olarak iki katına çıkar. "89.95f"deki gibi bir float değerini belirtmek için "f" kullanın.

/* Compile options needed: none
*/ 

#include <stdio.h>

void main()
   {
      float floatvar;
      double doublevar;

/* Print double constant. */ 
      printf("89.95 = %f\n", 89.95);      // 89.95 = 89.950000

/* Printf float constant */ 
      printf("89.95 = %f\n", 89.95F);     // 89.95 = 89.949997

/*** Use double constant. ***/ 
      floatvar = 89.95;
      doublevar = 89.95;

printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.950000

/*** Use float constant. ***/ 
      floatvar = 89.95f;
      doublevar = 89.95f;

printf("89.95 = %f\n", floatvar);   // 89.95 = 89.949997
      printf("89.95 = %lf\n", doublevar); // 89.95 = 89.949997
   }