SORU
7 Mayıs 2015, PERŞEMBE


Neden bazı yüzer < karşılaştırmalar diğerlerine göre dört kat daha yavaş tamsayı vardır?

Tamsayılar için yüzer karşılaştırırken, bazı değerler çift değerlendirilecek çok daha uzun bir benzer büyüklükteki diğer değerleri daha al.

Örneğin:

>>> import timeit
>>> timeit.timeit("562949953420000.7 < 562949953421000") # run 1 million times
0.5387085462592742

Ama eğer şamandıra veya tamsayı daha küçük ya da daha büyük bir miktar olarak yapılıyorsa, karşılaştırma çok daha hızlı bir şekilde çalışır:

>>> timeit.timeit("562949953420000.7 < 562949953422000") # integer increased by 1000
0.1481498428446173
>>> timeit.timeit("562949953423001.8 < 562949953421000") # float increased by 3001.1
0.1459577925548956

Karşılaştırma operatörü (örneğin yerine == > kullanarak) değişen belirgin bir şekilde kat etkilemez.

Bu değilsadecedaha büyük ya da daha küçük değerler alıyorum çünkü büyüklüğü ile ilgili bazı talihsiz şekilde olduğunu sanıyorum çok daha hızlı karşılaştırmalar neden olabilir bit hattı.

Açıkça, bu değerler karşılaştırıldığında hızlı daha yeterli çoğu durumlarda kullanmak için. Sadece Python diğerleri ile daha değerleri bazı çiftleri daha fazla mücadele gibi görünüyor neden olarak merak ediyorum.

CEVAP
7 Mayıs 2015, PERŞEMBE


Yüzen nesneler için Python kaynak kod içinde bir yorum olduğunu kabul eder:

Comparison is pretty much a nightmare

Bu, tersine yüzer, Python tamsayılar keyfi büyük olabilir, çünkü bir tamsayı için bir kayan nokta karşılaştırma, doğrudur ve her zaman kesin değildir. Bir yüzer tamsayı döküm çalışırken hassas kaybetmek ve karşılaştırma yapmak yanlış olabilir. Bir tamsayı için kaydır atmak için çalışıyor herhangi bir kesirli kısmı kaybolacak, çünkü her iki işe yaramayacak.

Bu sorunu geçici olarak almak için, Python kontrolleri bir dizi, eğer denetimlerden biri başarılı olursa sonucu dönen gerçekleştirir. Tamsayı çok büyük "" bir kayan nokta, tamsayı uzunluğu şamandıra üs karşılaştırır. de olsa iki değer belirtileri karşılaştırır Eğer tüm bu kontrolleri başarısız olursa, iki yeni Python nesneleri sonuç elde etmek için karşılaştırmak oluşturmak için gereklidir.

Bir şamandıra karşılaştırırken bir tamsayı için v uzun w, en kötü durum o/:

  • v w aynı işaretli (pozitif ya da negatif her ikisi de)
  • tamsayı w size_t türü (genellikle 32 veya 64 bit) düzenlenen, olabilir yeterli birkaç bit vardır
  • tamsayı w en az 49 bit vardır
  • şamandıra üs v w bit sayısı aynıdır.

Ve bu soru değerleri için elimizde tam olarak ne olduğunu:

>>> import math
>>> math.frexp(562949953420000.7) # gives the float's (significand, exponent) pair
(0.9999999999976706, 49)
>>> (562949953421000).bit_length()
49

49 şamandıra üs ve tamsayı bit sayısını iki olduğunu görüyoruz. Hem sayılar pozitif ve dört kriterlerinin üzerinde bir araya geldi.

Seçimi değerleri için daha büyük (veya daha küçük) değiştirebilir sayıda bit tamsayı veya üs değeri, ve Python olduğunu tespit etme sonucu karşılaştırma olmadan sahne pahalı son kontrol.

Bu dil CPython uygulaması için geçerlidir.


Daha detaylı karşılaştırma

float_richcompare bu fonksiyon iki değer v w arasında karşılaştırma işler.

Aşağıda işlevi gerçekleştiren kontroller adım adım açıklama. Python kaynağı açıklamalarda işlevi ne yaptığını anlamaya çalışırken aslında çok yardımsever, gerektiğinde onları bıraktım. Ayrıca cevap dibinde bir listede bu kontroller özetlenmiştir ettim.

Ana fikir için harita Python nesneleri v w iki uygun C çiftler i j, o zaman kolayca karşılaştırıldığında vermek doğru sonuç. Python 2 ve Python 3 de bunu yapmak için aynı düşünceleri (eski sadece ayrı ayrı int long halleden) kullanın.

Yapmanız gereken ilk şey, v kesinlikle Python bir şamandıra ve bir C i çift için harita kontrol edin. Fonksiyonu w da şamandıra ve haritalar olup olmadığını inceliyor sonraki bir C çift 31**. Bu tüm diğer kontroller atlanması olarak işlev için en iyi durum senaryosu. İşlevi de v inf nan olup olmadığını görmek için denetler:

static PyObject*
float_richcompare(PyObject *v, PyObject *w, int op)
{
    double i, j;
    int r = 0;
    assert(PyFloat_Check(v));       
    i = PyFloat_AS_DOUBLE(v);       

    if (PyFloat_Check(w))           
        j = PyFloat_AS_DOUBLE(w);   

    else if (!Py_IS_FINITE(i)) {
        if (PyLong_Check(w))
            j = 0.0;
        else
            goto Unimplemented;
    }

Şimdi ise w Bu kontroller başarısız olursa, bir Python şamandıra olmadığını biliyoruz. Eğer Python bir tam sayı ise fonksiyon kontrolleri artık. Bu durumda, en kolay test etmek özü işareti v ve işareti w (*dön 39* sıfır, -1 negatif, 1 eğer pozitif). Eğer işaretler farklı ise, bu tüm bilgileri karşılaştırma sonucunu döndürmek için gerekli:

    else if (PyLong_Check(w)) {
        int vsign = i == 0.0 ? 0 : i < 0.0 ? -1 : 1;
        int wsign = _PyLong_Sign(w);
        size_t nbits;
        int exponent;

        if (vsign != wsign) {
            /* Magnitudes are irrelevant -- the signs alone
             * determine the outcome.
             */
            i = (double)vsign;
            j = (double)wsign;
            goto Compare;
        }
    }   

Eğer bu onay başarısız olursa, o zaman v w aynı işaret var.

Bir sonraki onay tamsayı w bit sayısını sayar. Eğer çok fazla parçası var, eğer o zaman muhtemelen bir şamandıra olarak kabul edilemez ve bu yüzden yüzer büyüklüğü daha büyük olmalıdır v:

    nbits = _PyLong_NumBits(w);
    if (nbits == (size_t)-1 && PyErr_Occurred()) {
        /* This long is so large that size_t isn't big enough
         * to hold the # of bits.  Replace with little doubles
         * that give the same outcome -- w is so large that
         * its magnitude must exceed the magnitude of any
         * finite float.
         */
        PyErr_Clear();
        i = (double)vsign;
        assert(wsign != 0);
        j = wsign * 2.0;
        goto Compare;
    }

Eğer tamsayı w 48 veya daha az bit varsa, diğer taraftan, güvenli bir C ihbar çift j ve göre:

    if (nbits <= 48) {
        j = PyLong_AsDouble(w);
        /* It's impossible that <= 48 bits overflowed. */
        assert(j != -1.0 || ! PyErr_Occurred());
        goto Compare;
    }

Bu noktadan itibaren, w 49 veya daha fazla bit olduğunu biliyoruz. Pozitif bir tamsayı w tedavi için uygun olacaktır, işaret ve karşılaştırma gerekli operatör değiştirme:

    if (nbits <= 48) {
        /* "Multiply both sides" by -1; this also swaps the
         * comparator.
         */
        i = -i;
        op = _Py_SwappedOp[op];
    }

Şimdi işlevi şamandıra üs bakar. Bir mala * 2 significand olarak yazılı bir delil yok sayarak) olabilir hatırlayınüsve significand 0.5 ve 1 arasında bir sayı gösterir:

    (void) frexp(i, &exponent);
    if (exponent < 0 || (size_t)exponent < nbits) {
        i = 1.0;
        j = 2.0;
        goto Compare;
    }

Bu iki şeyi kontrol eder. Eğer üs değeri 0'dan küçük ise yüzer (ve herhangi bir tamsayı daha büyüklüğü içinde çok küçük) 1'den küçüktür. Ya da, eğer üs w bit sayısı az ise o zaman v < |w| beri * 2 significand varüs2'den daha aznbits.

Bu iki kontrolleri yapmadıkları, işlevi üs w bit sayısından daha büyük olup olmadığına bakıyor. Bu * 2 significand gösteriyorüs2'den büyüknbitsve böylece v > |w|:

    if ((size_t)exponent > nbits) {
        i = 2.0;
        j = 1.0;
        goto Compare;
    }

Eğer bu denetimi başarılı mı yoksa şamandıra üs v tamsayı w bit sayısı ile aynı olduğunu biliyoruz.

İki değer mukayese edilebilir tek yolu artık v w iki yeni Python tamsayıları inşa edecek. Fikri v çift tamsayı kısmı, kesirli kısmını atmak ve bir ekleyin. w da iki katına çıkarılır ve bu iki yeni Python nesneleri doğru dönüş değeri vermek için karşılaştırılabilir. Küçük değerler, 4.65 < 4 örneği ile karşılaştırma (2*4) 1 == 9 < 8 == (2*4) (false dönen) tarafından belirlenecektir.

    {
        double fracpart;
        double intpart;
        PyObject *result = NULL;
        PyObject *one = NULL;
        PyObject *vv = NULL;
        PyObject *ww = w;

        // snip

        fracpart = modf(i, &intpart); // split i (the double that v mapped to)
        vv = PyLong_FromDouble(intpart);

        // snip

        if (fracpart != 0.0) {
            /* Shift left, and or a 1 bit into vv
             * to represent the lost fraction.
             */
            PyObject *temp;

            one = PyLong_FromLong(1);

            temp = PyNumber_Lshift(ww, one); // left-shift doubles an integer
            ww = temp;

            temp = PyNumber_Lshift(vv, one);
            vv = temp;

            temp = PyNumber_Or(vv, one); // a doubled integer is even, so this adds 1
            vv = temp;
        }
        // snip
    }
}

Kısalık için ek hata denetimi bırakmadım ve Çöp izleme Python bu yeni nesneler oluşturur. Söylemeye gerek yok, bu ek yük ekler ve değerleri soruda vurgulanan diğerlerine göre karşılaştırmak için önemli ölçüde daha yavaş olduğunu açıklıyor.


Burada karşılaştırma işlevi tarafından gerçekleştirilen denetimler bir özet.

v bir şamandıra olalım ve C çift olarak koyun. Eğer w da bir kayan nokta ise:

  • w nan inf olup olmadığını kontrol edin. Eğer öyleyse, bu özel durumda ayrı ayrı w türüne bağlı olarak işlemek.

  • Değilse, doğrudan C çiftler gibi ifadeleri tarafından v w karşılaştırın.

Eğer w bir tamsayı ise:

  • v w belirtileri ayıklayın. Eğer farklıysa o zaman v w büyük değeri olan ve farklı olduğunu biliyoruz.

  • (Belirtileri aynı.) w çok fazla bir kaydır (daha fazla size_t) bit olup olmadığını kontrol edin. , 84 ** v daha büyük bir büyüklük vardır.

  • w 48 veya daha az bit olup olmadığını kontrol edin. Eğer öyleyse, güvenli bir şekilde v ile karşılaştırıldığında hassasiyet kaybetmeden bir C çift ve döküm olabilir.

  • (w 48 daha fazla bit vardır. Şimdi op uygun olarak karşılaştırmak pozitif bir tamsayı değiştirmiş olması gibi w davranacağız.)

  • Şamandıra üs v düşünün. Eğer üs negatif ise, o zaman v az daha 1 ve herhangi bir pozitif tamsayı daha bu nedenle daha az. Eğer üs w bit sayısı az ise başka, o zaman w daha az olmalı.

  • Eğer v üs w bit sayısı ne kadar büyükse o v w daha büyük.

  • (Üs w bit sayısı aynıdır.)

  • Son kontrol edin. Tam sayı ve kesirli kısımları içine v bölünmüş. Tamsayı kısmı çift ve 1 kesirli kısmını telafi etmek için ekleyin. Şimdi çift tamsayı w. Bu iki yeni tamsayılar sonuç almak yerine karşılaştırın.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Bokeh

    Bokeh

    9 HAZİRAN 2014
  • jesiel santos

    jesiel santo

    15 Ocak 2009
  • Tek Syndicate

    Tek Syndicat

    23 Temmuz 2008