En kesin sonucu elde etmek için şamandıralar hangi sırayla eklenmelidir?


105

Bu son röportajımda sorulan bir soruydu ve bilmek istiyorum (sayısal analizin teorisini aslında hatırlamıyorum, bu yüzden lütfen bana yardım edin :)

Kayan nokta sayılarını biriktiren bir fonksiyonumuz varsa:

std::accumulate(v.begin(), v.end(), 0.0);

va, std::vector<float>örneğin,.

  • Bu sayıları toplamadan önce sıralamak daha iyi olur mu?

  • En kesin cevabı hangi sıra verir?

Ben şüpheli sıralama artan düzende numaraları aslında sayısal hata yapar az , ama ne yazık ki ben kendim ispat edemez.

Not: Bunun muhtemelen gerçek dünya programlamayla hiçbir ilgisi olmadığının farkındayım, sadece merak ediyorum.


17
Bunun aslında gerçek dünya programlama ile ilgisi var. Bununla birlikte, birçok uygulama, 'oldukça yakın' olduğu sürece, hesaplamanın mutlak en iyi doğruluğunu gerçekten DİKKAT ETMEZ. Mühendislik uygulamaları? Son derece önemli. Tıbbi uygulamalar? Son derece önemli. Büyük ölçekli istatistikler? Biraz daha az doğruluk kabul edilebilir.
Zéychin

18
Lütfen gerçekten bilmediğiniz ve gerekçenizi ayrıntılı olarak açıklayan bir sayfayı gösteremediğiniz sürece cevap vermeyin. Etrafta uçan kayan nokta sayıları hakkında zaten çok fazla saçmalık var; buna eklemek istemiyoruz. Bildiğini sanıyorsan. DUR. çünkü sadece bildiğinizi sanıyorsanız, muhtemelen yanılıyorsunuz.
Martin York

4
@ Zéychin "Mühendislik uygulamaları? Son derece önemli. Tıbbi uygulamalar? Son derece önemli." ??? Gerçeği bilseydin şaşıracağını düşünüyorum :)
BЈовић

3
@Zeychin Mutlak hata konu dışıdır. Önemli olan göreceli hatadır. Bir radyanın birkaç yüzde biri% 0,001 ise, o zaman kimin umurunda?
BЈовић

3
Şu okumayı gerçekten tavsiye ederim: "her bilgisayar bilimcisinin kayan nokta hakkında bilmesi gerekenler
Mohammad Alaggan

Yanıtlar:


108

İçgüdünüz temelde doğrudur, artan düzende (büyüklükte) sıralama genellikle işleri biraz iyileştirir. Tek duyarlıklı (32 bit) kayan sayılar eklediğimiz durumu düşünün ve 1 / (1 milyar) 'a eşit 1 milyar değer ve 1'e eşit bir değer vardır. 1 önce gelirse, toplam gelir 1 + (1/1 milyar) 1 olduğundan hassasiyet kaybı nedeniyle 1'e kadar. Her eklemenin toplam üzerinde hiçbir etkisi yoktur.

Küçük değerler önce gelirse, en azından toplamları bir şeye dönüşürler, ancak o zaman bile 2 ^ 30 tane var, oysa 2 ^ 25 ya da öylesine sonra, her birinin ayrı ayrı toplamı etkilemediği duruma geri döndüm. artık. Bu yüzden hala daha fazla numaraya ihtiyacım olacak.

Bu uç bir durumdur, ancak genel olarak benzer büyüklükte iki değer eklemek, çok farklı büyüklükte iki değer eklemekten daha doğrudur, çünkü bu şekilde daha küçük değerde daha az kesinlik bitini "atarsınız". Sayıları sıralayarak, benzer büyüklükteki değerleri bir arada gruplandırırsınız ve bunları artan sırada ekleyerek, küçük değerlere kümülatif olarak büyük sayıların büyüklüğüne ulaşma "şansı" verirsiniz.

Yine de, negatif sayılar söz konusuysa, bu yaklaşımı "alt etmek" kolaydır. Toplamak için üç değer düşünün {1, -1, 1 billionth}. Aritmetik olarak doğru toplam 1 billionth, ancak ilk toplamam küçük bir değeri içeriyorsa, o zaman son toplamım 0 olacaktır. 6 olası emirden sadece 2'si "doğru" - {1, -1, 1 billionth}ve {-1, 1, 1 billionth}. 6 siparişin tümü, girdideki en büyük büyüklük değeri ölçeğinde (% 0.0000001 dışarı) doğru sonuçlar verir, ancak bunların 4'ü için sonuç, gerçek çözüm ölçeğinde yanlıştır (% 100 dışarı). Çözdüğünüz belirli sorun, size eski sorunun yeterince iyi olup olmadığını söyleyecektir.

Aslında, sıralı sırayla eklemekten çok daha fazla numara oynayabilirsiniz. Çok sayıda çok küçük değeriniz, orta sayıda orta değeriniz ve az sayıda büyük değeriniz varsa, o zaman önce tüm küçük olanları toplamak, sonra orta olanları ayrı ayrı toplamak, bu iki toplamı eklemek en doğru olabilir. sonra büyük olanları ekleyin. Kayan nokta eklemelerinin en doğru kombinasyonunu bulmak hiç de önemsiz değil, ancak gerçekten kötü durumlarla başa çıkmak için, farklı büyüklüklerde bir dizi toplamları tutabilir, her yeni değeri toplama büyüklüğüne en iyi uyan toplama ekleyebilir, ve bir değişen toplam, büyüklüğü için çok büyük olmaya başladığında, onu bir sonraki toplama ekleyin ve yeni bir toplam başlatın. Mantıksal olarak en uç noktaya bakıldığında, bu işlem, toplamı rastgele hassasiyetli bir türde gerçekleştirmeye eşdeğerdir (yani, d yapın). Ancak, artan veya azalan büyüklük sırasına göre eklemenin basit seçimi göz önüne alındığında, artan daha iyi bir seçimdir.

Gerçek dünya programlamayla bir ilgisi vardır, çünkü kazara her biri ayrı ayrı etkilenemeyecek kadar küçük olan çok sayıda değerden oluşan "ağır" bir kuyruğu keserseniz, hesaplamanızın çok kötü bir şekilde yanlış gidebileceği bazı durumlar vardır. toplam veya tek tek toplamın son birkaç bitini etkileyen çok sayıda küçük değerden çok fazla kesinlik atarsanız. Kuyruğun ihmal edilebilir olduğu durumlarda, muhtemelen umursamıyorsunuzdur. Örneğin, ilk etapta yalnızca az sayıda değeri bir araya ekliyorsanız ve toplamın yalnızca birkaç önemli rakamını kullanıyorsanız.


8
Açıklama için +1. Ekleme genellikle sayısal olarak kararlı olduğundan (çıkarma ve bölme işleminin aksine) bu biraz mantıksızdır.
Konrad Rudolph

2
@Konrad, sayısal olarak kararlı olabilir, ancak farklı işlenen büyüklükleri verildiğinde kesin değildir :)
MSN

3
@ 6502: büyüklük sırasına göre sıralanırlar, bu nedenle -1 gelir. Toplamın gerçek değeri 1 büyüklüğündeyse, sorun değil. Üç değeri toplarsanız: 1 / milyar, 1 ve -1, o zaman 0 elde edersiniz, bu noktada ilginç pratik soruyu yanıtlamanız gerekir - ölçeğinde doğru olan bir cevaba ihtiyacınız var mı? doğru toplam mı yoksa yalnızca en büyük değerler ölçeğinde doğru olan bir yanıta mı ihtiyacınız var? Bazı pratik uygulamalar için ikincisi yeterince iyidir, ancak uygun olmadığında daha sofistike bir yaklaşıma ihtiyacınız vardır. Kuantum fiziği renormalizasyonu kullanır.
Steve Jessop

8
Bu basit şemaya sadık kalacaksanız, her zaman en düşük büyüklüğe sahip iki sayıyı toplayacağım ve toplamı kümeye yeniden yerleştireceğim. (Muhtemelen bir birleştirme sıralaması burada en iyi sonucu verir. Dizinin önceden toplanan sayıları içeren kısmını kısmi toplamlar için bir çalışma alanı olarak kullanabilirsiniz.)
Neil

2
@Kevin Panko: Basit versiyon, tek duyarlıklı bir şamandıranın 24 ikili rakama sahip olmasıdır, bunların en büyüğü sayıdaki en büyük set bitidir. Dolayısıyla, büyüklükleri 2 ^ 24'ten fazla farklı olan iki sayıyı bir araya getirirseniz, daha küçük olan değerin toplam kaybına uğrarsınız ve büyüklük bakımından daha küçük bir farklılık gösterirlerse, daha küçük olanın karşılık gelen doğruluk bit sayısını kaybedersiniz. numara.
Steve Jessop

88

Ayrıca muhtemelen farkında olmanız gereken Kahan Summation adı verilen bu tür bir biriktirme işlemi için tasarlanmış bir algoritma da vardır .

Wikipedia'ya göre,

Kahan toplama algoritması (aynı zamanda telafi toplamı ) önemli ölçüde belirgin yaklaşıma göre, sınırlı duyarlık kayan nokta bir sayı dizisi eklenmesi ile elde edilen, toplam sayısal hatayı azaltır. Bu, ayrı bir çalışma telafisi (küçük hataları biriktirmek için bir değişken) tutarak yapılır.

Sözde kodda algoritma şu şekildedir:

function kahanSum(input)
 var sum = input[1]
 var c = 0.0          //A running compensation for lost low-order bits.
 for i = 2 to input.length
  y = input[i] - c    //So far, so good: c is zero.
  t = sum + y         //Alas, sum is big, y small, so low-order digits of y are lost.
  c = (t - sum) - y   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
  sum = t             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
 next i               //Next time around, the lost low part will be added to y in a fresh attempt.
return sum

3
Bu konuya +1 güzel ek. Bu ifadeleri "hevesle optimize eden" herhangi bir derleyici yasaklanmalıdır.
Chris A.

1
İki toplama değişkeni sumve cfarklı büyüklükteki kesinliği neredeyse ikiye katlamak için basit bir yöntemdir . N değişkene önemsiz şekilde genişletilebilir.
MSalters

2
@ChrisA. bunu, sayılan tüm derleyiciler üzerinde açıkça kontrol edebilirsiniz (örn -ffast-math. GCC üzerinden).
Konrad Rudolph

6
@Konrad Rudolph bunun olası bir optimizasyon olduğunu belirttiğiniz için teşekkürler -ffast-math. Bu tartışmadan ve bu bağlantıdan öğrendiğim şey, sayısal doğruluğu önemsiyorsanız muhtemelen kullanmaktan kaçınmanız gerektiğidir, -ffast-mathancak CPU'ya bağlı olabileceğiniz ancak kesin sayısal hesaplamaları önemsemediğiniz birçok uygulamada (örneğin oyun programlama ), -ffast-mathkullanımı mantıklı. Bu nedenle, şiddetle ifade edilen "yasaklı" yorumumu değiştirmek istiyorum.
Chris A.

Çift duyarlıklı değişkenlerin kullanılması sum, c, t, yyardımcı olacaktır. Ayrıca sum -= cdaha önce eklemeniz gerekir return sum.
G. Cohen

34

Steve Jessop'un verdiği cevaptaki aşırı örneği denedim.

#include <iostream>
#include <iomanip>
#include <cmath>

int main()
{
    long billion = 1000000000;
    double big = 1.0;
    double small = 1e-9;
    double expected = 2.0;

    double sum = big;
    for (long i = 0; i < billion; ++i)
        sum += small;
    std::cout << std::scientific << std::setprecision(1) << big << " + " << billion << " * " << small << " = " <<
        std::fixed << std::setprecision(15) << sum <<
        "    (difference = " << std::fabs(expected - sum) << ")" << std::endl;

    sum = 0;
    for (long i = 0; i < billion; ++i)
        sum += small;
    sum += big;
    std::cout  << std::scientific << std::setprecision(1) << billion << " * " << small << " + " << big << " = " <<
        std::fixed << std::setprecision(15) << sum <<
        "    (difference = " << std::fabs(expected - sum) << ")" << std::endl;

    return 0;
}

Şu sonucu aldım:

1.0e+00 + 1000000000 * 1.0e-09 = 2.000000082740371    (difference = 0.000000082740371)
1000000000 * 1.0e-09 + 1.0e+00 = 1.999999992539933    (difference = 0.000000007460067)

İlk satırdaki hata ikinci satırda on kattan fazla.

Şunu değiştirirsem doubleiçin s floatyukarıdaki kodda s, alıyorum:

1.0e+00 + 1000000000 * 1.0e-09 = 1.000000000000000    (difference = 1.000000000000000)
1000000000 * 1.0e-09 + 1.0e+00 = 1.031250000000000    (difference = 0.968750000000000)

Her iki yanıt da 2.0'a yakın bile değil (ancak ikincisi biraz daha yakın).

doubleDaniel Pryden tarafından açıklandığı gibi Kahan toplamını (ile ) kullanarak:

#include <iostream>
#include <iomanip>
#include <cmath>

int main()
{
    long billion = 1000000000;
    double big = 1.0;
    double small = 1e-9;
    double expected = 2.0;

    double sum = big;
    double c = 0.0;
    for (long i = 0; i < billion; ++i) {
        double y = small - c;
        double t = sum + y;
        c = (t - sum) - y;
        sum = t;
    }

    std::cout << "Kahan sum  = " << std::fixed << std::setprecision(15) << sum <<
        "    (difference = " << std::fabs(expected - sum) << ")" << std::endl;

    return 0;
}

Tam olarak 2.0 alıyorum:

Kahan sum  = 2.000000000000000    (difference = 0.000000000000000)

Ve değiştirseniz bile doubleüzere s floatyukarıdaki kodda s, alıyorum:

Kahan sum  = 2.000000000000000    (difference = 0.000000000000000)

Görünüşe göre Kahan gitmenin yolu!


Benim "büyük" değerim 1e9'a değil 1'e eşit. Artan boyut sırasına eklenen ikinci cevabınız matematiksel olarak doğrudur (1 milyar artı milyarda biri 1 milyar ve 1'dir), ancak şans eseri yöntemin herhangi bir genel sağlamlığı :-) doubleKötü acı çekmediğini unutmayın IEEE'de floatyalnızca 24 ve 24 tane olduğu için, 52 önemli bit içerdiğinden milyarda birini bir araya getirirken hassasiyet kaybı .
Steve Jessop

@Steve, hatam, özür dilerim. Örnek kodu amaçladığınız şekilde güncelledim.
Andrew Stein

4
Kahan'ın hala sınırlı bir hassasiyeti var, ancak katil bir durum oluşturmak için hem ana toplama hem de hata toplayıcıya cihtiyacınız var ve bir sonraki özetten çok daha büyük değerler içeriyor. Bu, özetin ana meblağdan çok çok daha küçük olduğu anlamına gelir, bu yüzden çok fazla toplanması için çok fazla sayıda olması gerekecek. Özellikle doublearitmetik ile.
Steve Jessop

14

Verileri sıralamak veya başka bir şekilde yeniden düzenlemek gerekmeksizin bu sorunu çözen bir algoritma sınıfı vardır .

Başka bir deyişle, veri üzerinden tek geçişte toplama yapılabilir. Bu ayrıca bu tür algoritmaları, veri setinin önceden bilinmediği durumlarda, örneğin verilerin gerçek zamanlı olarak ulaşması ve devam eden toplamın muhafaza edilmesi gerektiği durumlarda uygulanabilir kılar.

İşte yakın tarihli bir makalenin özeti:

Kayan noktalı sayıların tam olarak toplanması için yeni, çevrimiçi bir algoritma sunuyoruz. "Çevrimiçi" derken, algoritmanın bir seferde yalnızca bir giriş görmesi gerektiğini ve yalnızca sabit bellek gerektirirken bu tür girişlerin keyfi uzunlukta giriş akışını alabileceğini kastediyoruz. "Kesin" ile, algoritmamızın dahili dizisinin toplamının tüm girdilerin toplamına tam olarak eşit olduğunu ve döndürülen sonucun doğru şekilde yuvarlanmış toplam olduğunu kastediyoruz. Doğruluğun kanıtı tüm girdiler için geçerlidir (normalize edilmemiş sayılar, ancak modulo ara taşma dahil) ve toplamın koşul numarasından veya toplamın sayısından bağımsızdır. Algoritma asimptotik olarak toplamda yalnızca 5 FLOP'a ihtiyaç duyar ve komut düzeyinde paralellik nedeniyle, bariz olandan yalnızca yaklaşık 2-3 kat daha yavaş çalışır, Summand sayısı 10.000'den fazla olduğunda hızlı ama aptalca "sıradan yinelemeli toplama" döngüsü. Bu nedenle, bilgimize göre, bilinen algoritmalar arasında en hızlı, en doğru ve en verimli hafızadır. Aslında, daha hızlı bir algoritmanın veya önemli ölçüde daha az FLOP gerektiren bir algoritmanın donanım geliştirmeleri olmadan nasıl var olabileceğini görmek zordur. Çok sayıda zirve için başvuru sağlanmaktadır.

Kaynak: Algoritma 908: Kayan Noktalı Akışların Çevrimiçi Tam Toplamı .


1
@Inverse: Etrafta hala tuğla ve harç kütüphaneleri var. Alternatif olarak, PDF'yi çevrimiçi satın almanın maliyeti 5-15 ABD dolarıdır (ACM üyesi olup olmadığınıza bağlı olarak). Son olarak, DeepDyve gazeteyi 2,99 $ karşılığında 24 saat ödünç vermeyi teklif ediyor gibi görünüyor (DeepDyve'de yeniyseniz, ücretsiz denemelerinin bir parçası olarak ücretsiz olarak da alabilirsiniz): deepdyve.com/lp/acm /…
NPE

2

Steve'in önce sayıları artan sırada sıralama cevabına dayanarak, iki fikir daha sunacağım:

  1. Üstündeki iki sayının üslerindeki farka karar verin ki bu farkın çok fazla hassasiyet kaybedeceğine karar verebilirsiniz.

  2. Ardından, toplayıcının üssü bir sonraki sayı için çok büyük olana kadar sayıları ekleyin, ardından toplayıcıyı geçici bir sıraya koyun ve bir sonraki sayı ile toplayıcıyı başlatın. Orijinal listeyi bitirene kadar devam edin.

Süreci geçici kuyrukla (sıralayarak) ve muhtemelen daha büyük bir üs farkıyla tekrar edersiniz.

Her zaman üsleri hesaplamanız gerekiyorsa, bunun oldukça yavaş olacağını düşünüyorum.

Bir programla hızlı bir şekilde gittim ve sonuç 1.99903 idi


2

Sayıları biriktirmeden önce sıralamaktan daha iyisini yapabileceğinizi düşünüyorum, çünkü biriktirme sürecinde biriktirici büyüyor ve büyüyor. Çok sayıda benzer sayıya sahipseniz, hassasiyeti hızla kaybetmeye başlayacaksınız. İşte bunun yerine önereceğim şey:

while the list has multiple elements
    remove the two smallest elements from the list
    add them and put the result back in
the single element in the list is the result

Elbette bu algoritma, bir liste yerine öncelikli bir kuyrukla daha verimli olacaktır. C ++ kodu:

template <typename Queue>
void reduce(Queue& queue)
{
    typedef typename Queue::value_type vt;
    while (queue.size() > 1)
    {
        vt x = queue.top();
        queue.pop();
        vt y = queue.top();
        queue.pop();
        queue.push(x + y);
    }
}

sürücü:

#include <iterator>
#include <queue>

template <typename Iterator>
typename std::iterator_traits<Iterator>::value_type
reduce(Iterator begin, Iterator end)
{
    typedef typename std::iterator_traits<Iterator>::value_type vt;
    std::priority_queue<vt> positive_queue;
    positive_queue.push(0);
    std::priority_queue<vt> negative_queue;
    negative_queue.push(0);
    for (; begin != end; ++begin)
    {
        vt x = *begin;
        if (x < 0)
        {
            negative_queue.push(x);
        }
        else
        {
            positive_queue.push(-x);
        }
    }
    reduce(positive_queue);
    reduce(negative_queue);
    return negative_queue.top() - positive_queue.top();
}

Kuyruktaki sayılar negatiftir çünkü topen büyük sayıyı verir , ancak biz en küçüğü istiyoruz . Kuyruğa daha fazla şablon argümanı sağlayabilirdim, ancak bu yaklaşım daha basit görünüyor.


2

Bu, sorunuzu tam olarak yanıtlamaz, ancak yapılacak akıllıca bir şey, toplamı iki kez çalıştırmaktır; bir kez yuvarlama modu "yukarı yuvarlama " ve bir kez "yuvarlama" ile. İki yanıtı karşılaştırın ve sonuçlarınızın ne olduğunu biliyorsunuz / nasıl / yanlış ve bu nedenle daha akıllı bir toplama stratejisi kullanmanız gerekiyorsa. Ne yazık ki çoğu dil kayan nokta yuvarlama modunu olması gerektiği kadar kolay hale getirmiyor çünkü insanlar bunun günlük hesaplamalarda gerçekten yararlı olduğunu bilmiyorlar.

Devam ederken en yüksek ve en düşük değerleri koruyarak, bunun gibi tüm matematik işlemlerini yaptığınız Aralık aritmetiğine bir göz atın . Bazı ilginç sonuçlara ve optimizasyonlara yol açar.


0

Doğruluğu artıran en basit sıralama , artan mutlak değere göre sıralamaktır. Bu, en küçük büyüklük değerlerinin, bir hassasiyet kaybını tetikleyebilecek daha büyük büyüklük değerleriyle etkileşime girmeden önce birikme veya iptal etme şansına sahip olmasını sağlar.

Bununla birlikte, birbiriyle çakışmayan birden çok kısmi toplamı izleyerek daha iyisini yapabilirsiniz. İşte tekniği açıklayan ve doğruluk kanıtı sunan bir makale: www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps

Bu algoritma ve tam kayan nokta toplamaya yönelik diğer yaklaşımlar basit Python'da şu adreste uygulanır: http://code.activestate.com/recipes/393090/ Bunlardan en az ikisi önemsiz bir şekilde C ++ 'ya dönüştürülebilir.


0

IEEE 754 tek veya çift duyarlıklı veya bilinen format numaraları için başka bir alternatif, üs tarafından indekslenmiş bir sayı dizisi (arayan tarafından geçen veya C ++ için bir sınıfta) kullanmaktır. Diziye sayılar eklerken, yalnızca aynı üslü sayılar eklenir (boş bir yuva bulunana ve sayı saklanana kadar). Bir toplam istendiğinde, kesmeyi en aza indirmek için dizi en küçükten en büyüğe toplanır. Tek hassas örnek:

/* clear array */
void clearsum(float asum[256])
{
size_t i;
    for(i = 0; i < 256; i++)
        asum[i] = 0.f;
}

/* add a number into array */
void addtosum(float f, float asum[256])
{
size_t i;
    while(1){
        /* i = exponent of f */
        i = ((size_t)((*(unsigned int *)&f)>>23))&0xff;
        if(i == 0xff){          /* max exponent, could be overflow */
            asum[i] += f;
            return;
        }
        if(asum[i] == 0.f){     /* if empty slot store f */
            asum[i] = f;
            return;
        }
        f += asum[i];           /* else add slot to f, clear slot */
        asum[i] = 0.f;          /* and continue until empty slot */
    }
}

/* return sum from array */
float returnsum(float asum[256])
{
float sum = 0.f;
size_t i;
    for(i = 0; i < 256; i++)
        sum += asum[i];
    return sum;
}

çift ​​kesinlik örneği:

/* clear array */
void clearsum(double asum[2048])
{
size_t i;
    for(i = 0; i < 2048; i++)
        asum[i] = 0.;
}

/* add a number into array */
void addtosum(double d, double asum[2048])
{
size_t i;
    while(1){
        /* i = exponent of d */
        i = ((size_t)((*(unsigned long long *)&d)>>52))&0x7ff;
        if(i == 0x7ff){         /* max exponent, could be overflow */
            asum[i] += d;
            return;
        }
        if(asum[i] == 0.){      /* if empty slot store d */
            asum[i] = d;
            return;
        }
        d += asum[i];           /* else add slot to d, clear slot */
        asum[i] = 0.;           /* and continue until empty slot */
    }
}

/* return sum from array */
double returnsum(double asum[2048])
{
double sum = 0.;
size_t i;
    for(i = 0; i < 2048; i++)
        sum += asum[i];
    return sum;
}

Bu, Malcolm 1971'in yöntemine veya daha çok Demmel ve Hida'nın üssünü kullanan varyantına benziyor ("Algoritma 3"). Dışarıda sizinki gibi taşıma tabanlı döngü yapan başka bir algoritma var, ancak şu anda bulamıyorum.
ZachB

@ZachB - kavram, bağlantılı liste için aşağıdan yukarıya birleştirme sıralamasına benzer , bu aynı zamanda küçük bir dizi kullanır; burada dizi [i], 2 ^ i düğümlü listeyi işaret eder. Bunun ne kadar geriye gittiğini bilmiyorum. Benim durumumda, 1970'lerde kendini keşfetmekti.
rcgldr

-1

Şamandıralarınız çift hassasiyetle eklenmelidir. Bu size diğer herhangi bir tekniğin sağlayabileceğinden daha fazla ek hassasiyet sağlayacaktır. Biraz daha hassas ve önemli ölçüde daha fazla hız için, örneğin dört toplam oluşturabilir ve bunları sonunda toplayabilirsiniz.

Çift duyarlıklı sayılar ekliyorsanız, toplam için long double kullanın - bununla birlikte, bu yalnızca long double'ın gerçekte iki katından daha fazla kesinliğe sahip olduğu uygulamalarda olumlu bir etkiye sahip olacaktır (tipik olarak x86, PowerPC derleyici ayarlarına bağlı olarak).


1
"Bu size diğer herhangi bir tekniğin sağlayabileceğinden daha fazla kesinlik verecektir" Cevabınızın, kesin toplamanın nasıl kullanılacağını açıklayan daha erken bir geç cevaptan bir yıldan fazla bir süre sonra geldiğini fark ediyor musunuz?
Pascal Cuoq

"Uzun çift" türü korkunç ve onu kullanmamalısın.
Jeff

-1

Sıralama ile ilgili olarak, bana öyle geliyor ki, eğer bir iptal bekliyorsanız, o zaman sayılar artan değil, azalan büyüklük sırasına göre eklenmelidir . Örneğin:

((-1 + 1) + 1e-20) 1e-20 verir

fakat

((1e-20 + 1) - 1) 0 verir

İlk denklemde iki büyük sayı iptal edilirken ikinci denklemde 1e-20 terimi 1'e eklendiğinde kaybolur, çünkü onu tutmak için yeterli hassasiyet yoktur.

Ayrıca, ikili toplama , çok sayıda sayıyı toplamak için oldukça uygundur.

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.