C ++ İşlevinde Sayısal Hatayı Çözümleme


20

Giriş olarak birkaç kayan nokta değeri (tek veya çift) alan, bazı hesaplama yapan ve çıktı kayan nokta değerleri (ayrıca tek veya çift) üreten bir işleve sahip olduğumu varsayalım. Öncelikle MSVC 2008 ile çalışıyorum, aynı zamanda MinGW / GCC ile çalışmayı da planlıyorum. C ++ ile programlama yapıyorum.

Sonuçlarda ne kadar hatanın programlı olarak ölçülmesinin tipik yolu nedir? Rasgele bir hassas kütüphane kullanmam gerektiğini varsayarsak: hızı önemsemediğimde en iyi kütüphane hangisidir?

Yanıtlar:


17

Yuvarlama hatanızda iyi bir sınır arıyorsanız, mutlaka kesin hassasiyetli bir kütüphaneye ihtiyacınız yoktur. Bunun yerine çalışan hata analizini kullanabilirsiniz.

İyi bir çevrimiçi referans bulamadım, ancak hepsi Nick Higham'ın "Sayısal Algoritmaların Doğruluğu ve Kararlılığı" kitabının Bölüm 3.3'ünde açıklandı. Fikir oldukça basit:

  1. Kodunuzu, her satırda tek bir aritmetik işlem için tek bir atamanız olacak şekilde yeniden çarpanlarına ayırın.
  2. Her değişken için, örneğin x, bir sabit atandığında x_errsıfıra başlatılan bir değişken oluşturun x.
  3. Her işlem için, örneğin z = x * y, değişken z_errkayan noktalı aritmetiğin standart modelini ve ortaya çıkan zve çalışan hataları x_errve y_err.
  4. Fonksiyonunuzun dönüş değeri de _errona bağlı bir değere sahip olmalıdır . Bu, toplam yuvarlama hatanızdaki verilere bağlı bir sınırdır.

Zor bölüm 3. adımdır. En basit aritmetik işlemler için aşağıdaki kuralları kullanabilirsiniz:

  • z = x + y -> z_err = u*abs(z) + x_err + y_err
  • z = x - y -> z_err = u*abs(z) + x_err + y_err
  • z = x * y -> z_err = u*abs(z) + x_err*abs(y) + y_err*abs(x)
  • z = x / y -> z_err = u*abs(z) + (x_err*abs(y) + y_err*abs(x))/y^2
  • z = sqrt(x) -> z_err = u*abs(z) + x_err/(2*abs(z))

u = eps/2birim yuvarlak nerede . Evet, kuralları +ve -aynıdır. Diğer herhangi bir işlem için kurallar, op(x)uygulanan sonucun Taylor serisi genişletmesi kullanılarak kolayca çıkarılabilir op(x + x_err). Veya googling'i deneyebilirsiniz. Ya da Nick Higham'ın kitabını kullanarak.

Örnek olarak, Horner şemasını kullanarak abir noktada katsayılardaki bir polinomları değerlendiren aşağıdaki Matlab / Octave kodunu düşünün x:

function s = horner ( a , x )
    s = a(end);
    for k=length(a)-1:-1:1
        s = a(k) + x*s;
    end

İlk adım olarak, iki işlemi şu bölümlere ayırdık s = a(k) + x*s:

function s = horner ( a , x )
    s = a(end);
    for k=length(a)-1:-1:1
        z = x*s;
        s = a(k) + z;
    end

Sonra _errdeğişkenleri tanıtırız. Not girişler olduğunu ave xkabul edilir tam olarak, ama biz sadece yanı da değerlerini tekabül geçmek için kullanıcı gerektirebilir a_errve x_err:

function [ s , s_err ] = horner ( a , x )
    s = a(end);
    s_err = 0;
    for k=length(a)-1:-1:1
        z = x*s;
        z_err = ...;
        s = a(k) + z;
        s_err = ...;
    end

Son olarak, hata koşullarını almak için yukarıda açıklanan kuralları uygularız:

function [ s , s_err ] = horner ( a , x )
    u = eps/2;
    s = a(end);
    s_err = 0;
    for k=length(a)-1:-1:1
        z = x*s;
        z_err = u*abs(z) + s_err*abs(x);
        s = a(k) + z;
        s_err = u*abs(s) + z_err;
    end

Hiçbir beri o Not a_errveya x_errörneğin, bunlar sıfır olduğu varsayılır, ilgili terimler basitçe hata ifadelerde göz ardı edilir.

Et voilà! Şimdi sonucun yanında verilere bağlı bir hata tahmini döndüren bir Horner şemasına sahibiz (not: bu hata üzerinde bir üst sınırdır ).

Bir yan not olarak, C ++ kullandığınız için, _errterim boyunca hareket eden kayan nokta değerleri için kendi sınıfınızı oluşturmayı ve bu değerleri yukarıda açıklandığı gibi güncellemek için tüm aritmetik işlemleri aşırı yüklemeyi düşünebilirsiniz . Büyük kodlar için, bu, hesaplama açısından daha az verimli de olsa daha kolay olabilir. Bunu söyledikten sonra, böyle bir sınıfı çevrimiçi bulabilirsiniz. Hızlı bir Google araması bana bu bağlantıyı verdi .

±ux(1±u) kayan noktada , yani aralığınız sayının kendisine yuvarlanır.


1
Bu analiz için +1, çünkü ilginç. Higham'ın çalışmasını seviyorum. Beni ilgilendiren şey, bir kullanıcının bu ekstra kodu elle yazmasını gerektirmesi (yarı otomatik olarak aralık aritmetiği yerine), sayısal işlemlerin sayısı arttıkça hataya açık olabilir.
Geoff Oxberry

1
@ GeoffOxberry: Karmaşıklık konusuna tamamen katılıyorum. Daha büyük kodlar için, her işlemi sadece bir kez doğru bir şekilde uygulamak zorunda olan çiftler üzerindeki işlemleri aşırı yükleyen bir sınıf / veri türü yazmanızı şiddetle tavsiye ederim. Matlab / Octave için böyle bir şey olmadığı için oldukça şaşırdım.
Pedro

Bu analizi seviyorum, ancak hata terimlerinin hesaplanması kayan noktalı de gerçekleştirildiğinden, kayan nokta hataları nedeniyle bu hata terimleri kesin olmayacak mı?
plasmacel

8

Keyfi hassas kayan nokta aritmetiği (ve daha fazlası) için güzel bir taşınabilir ve açık kaynak kütüphane Victor Shoup'un C ++ kaynak formunda bulunan NTL'sidir .

Daha düşük bir seviyede GNU Çoklu Hassasiyet (GMP) Bignum Kütüphanesi , aynı zamanda bir açık kaynak paketidir.

NTL GMP ile kullanılabilir daha hızlı performans gereklidir, ancak NTL "hız umurumda değil" kesinlikle kullanılabilir kendi temel rutinleri sağlar. GMP, "en hızlı bignum kütüphanesi" olduğunu iddia ediyor. GMP büyük ölçüde C ile yazılmıştır, ancak bir C ++ arayüzüne sahiptir.

Katma: Aralık aritmetiği otomatik olarak kesin yanıtın üst ve alt sınırlarını verebilirken, bu, aralığın boyutu tipik olarak her işlemde (göreceli veya mutlak hata algısı).

Yuvarlama hataları veya ayrıklaştırma hataları vb. İçin hata boyutunu bulmanın tipik yolu, ekstra bir hassasiyet değeri hesaplamak ve bunu "standart" hassasiyet değeri ile karşılaştırmaktır. Hata boyutunun kendisini makul doğrulukta belirlemek için sadece mütevazı ekstra hassasiyet gereklidir, çünkü yalnızca yuvarlama hataları "standart" hassasiyette ekstra hassasiyet hesaplamasından çok daha büyüktür.

Nokta, tek ve çift kesinlikli hesaplamaların karşılaştırılmasıyla gösterilebilir. C ++ ara ifadelerinin her zaman (en azından) çift kesinlikte hesaplandığını unutmayın, bu nedenle "saf" tek kesinlikteki bir hesaplamanın nasıl olacağını göstermek istiyorsak, ara değerleri tek kesinlikte saklamamız gerekir.

C kodu snippet'i

    float fa,fb;
    double da,db,err;
    fa = 4.0;
    fb = 3.0;
    fa = fa/fb;
    fa -= 1.0;

    da = 4.0;
    db = 3.0;
    da = da/db;
    da -= 1.0;

    err = fa - da;
    printf("Single precision error wrt double precision value\n");
    printf("Error in getting 1/3rd is %e\n",err);
    return 0;

Yukarıdaki çıktı (Cygwin / MinGW32 GCC takım zinciri):

Single precision error wrt double precision value
Error in getting 1/3rd is 3.973643e-08

Bu nedenle hata, 1 / 3'ün tek duyarlığa yuvarlanmasında ne beklediğiyle ilgilidir. Hatanın ölçümü kesinlik için değil, büyüklük için olduğundan, hatada birkaç ondalık basamaktan daha fazlasını almayı umursamıyorum (şüphelendim) .


Yaklaşımınız kesinlikle matematiksel olarak sağlam. Bence ödünler katıdır; hata hakkında bilgiç insanlar aralık aritmetiği sertliğine işaret edecek, ancak birçok uygulamada, ekstra hassasiyetle hesaplamanın yeterli olacağını ve sonuçta ortaya çıkan hata tahminlerinin muhtemelen daha sıkı olacağını düşünüyorum.
Geoff Oxberry

Kullanacağımı hayal ettiğim yaklaşım budur. Uygulamam için en uygun olanı görmek için bu farklı tekniklerden birkaçını deneyebilirim. Kod örneği güncellemesi çok takdir ediliyor!
user_123abc

7

GMP (yani, GNU Çoklu Hassasiyet kütüphanesi) bildiğim en iyi keyfi hassas kütüphanedir.

Keyfi kayan nokta fonksiyonu sonuçlarındaki hatayı ölçmek için herhangi bir programlı yol bilmiyorum. Deneyebileceğiniz bir şey, aralık aritmetiği kullanarak bir işlevin aralık uzantısını hesaplamaktır . C ++ 'da, aralık uzantılarını hesaplamak için bir tür kitaplık kullanmanız gerekir; böyle bir kütüphane Takviye Aralığı Aritmetik Kütüphanesidir.. Temel olarak, hatayı ölçmek için, işlev aralıklarınıza, ilgili değerlerde merkezlenmiş 2 kat birim yuvarlaklık (kabaca) genişliğine sahip bağımsız değişkenler olarak tedarik edersiniz ve sonra çıktınız aralıkların bir koleksiyonu, Bu da size hatanın muhafazakar bir tahminini verir. Bu yaklaşımla ilgili bir zorluk, bu şekilde kullanılan aralık aritmetiğinin hatayı önemli miktarlarda abartmasıdır, ancak bu yaklaşım aklıma gelen en "programlı" dır.


Ah, cevabında belirtilen aralık aritmetiği fark ettim ...
Ali

2
Richard Harris, ACCU Floating Point Blues hakkındaki Overload dergisinde çok sayıda makale yazdı . Aralık aritmetiği ile ilgili makalesi Overload 103'te yer almaktadır ( pdf , p19-24).
Mark Booth

6

Aralık analizi ile titiz ve otomatik hata tahmini yapılabilir . Sayılar yerine aralıklarla çalışıyorsunuz. Örneğin ekleme:

[a,b] + [c,d] = [min(a+c, a+d, b+c, b+d), max (a+c, a+d, b+c, b+d)] = [a+c, b+d]

Yuvarlama da titizlikle ele alınabilir, bkz. Yuvarlatılmış aralık aritmetiği .

Girdiniz dar aralıklardan oluştuğu sürece, tahminler iyidir ve hesaplanması oldukça ucuzdur. Ne yazık ki, hata genellikle fazla tahmin edilir, bkz. Bağımlılık sorunu .

Herhangi bir keyfi hassasiyet aralığı aritmetik kütüphanesi bilmiyorum.

Eldeki probleminize aralık aritmetiğinin ihtiyaçlarınızı karşılayıp karşılayamayacağına bağlıdır.


4

GNU MPFR kütüphanesi ana odak noktası olarak (kadar kolay göründüğü kadar değil tüm işlemler için, özellikle doğru yuvarlama üzere) yüksek doğruluk sahip bir rasgele-hassas float kütüphanedir. Kaputun altında GNU MP kullanır. Geoff'un cevabının önerdiği gibi - doğrulama amaçları için kullanışlı olabilecek aralık aritmetiği yapan MPFI adlı bir uzantıya sahiptir : ortaya çıkan aralık küçük sınırlar içine düşene kadar çalışma hassasiyetini artırmaya devam edin.

Ancak bu her zaman işe yaramaz; özellikle, her adımın yuvarlama sorunlarından bağımsız bir "hata" taşıdığı sayısal entegrasyon gibi bir şey yapıyorsanız mutlaka etkili değildir. Bu durumda, entegrasyon hatasını sınırlamak için belirli algoritmalar kullanarak (ve aralıklar yerine Taylor modelleri kullanarak) COZY sonsuzluğu gibi özel bir paket deneyin .


Katılıyorum; sayısal entegrasyon kesinlikle saf aralık aritmetiğinin ters gidebileceği bir durumdur. Ancak Taylor modelleri bile aralık aritmetiği kullanır. Ben Makino ve Berz'ın çalışmalarına aşinayım ve Taylor modelini RE Moore anlamında kullandıklarına inanıyorum, yine de “diferansiyel cebir” dediklerini içeren hileler kullanıyorlar.
Geoff Oxberry

@GeoffOxberry: Evet - Bence bu diferansiyel cebir, entegrasyon adımında hataya bağlanmak için bir şey.
Erik P.

0

Visual Studio ile çalışıyorsanız MPIR kullanmak için iyi bir kütüphane olduğu söylendi:

http://mpir.org/


SciComp.SE'ye hoş geldiniz! Kayan nokta hesaplama hatalarını ölçmek için bu kütüphanenin nasıl kullanılabileceğine dair bazı ayrıntılar ekleyebilir misiniz?
Christian Clason

Yapmaya çalışacağım; Aslında henüz bilgisayarımda MPIR kurmadım! GMP ve MPFR kurdum.
balıkçı
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.