Kayan nesneler için Python kaynak kodundaki bir yorum şunları kabul eder:
Karşılaştırma neredeyse bir kabus
Bu, bir şamandırayı bir tam sayı ile karşılaştırırken özellikle doğrudur, çünkü şamandıralardan farklı olarak, Python'daki tam sayılar keyfi olarak büyük olabilir ve her zaman kesindir. Tamsayıyı bir kayan noktaya yayınlamaya çalışmak hassasiyeti kaybedebilir ve karşılaştırmayı yanlış yapabilir. Şamandırayı bir tamsayıya atmaya çalışmak da işe yaramayacaktır çünkü herhangi bir kesirli kısım kaybolacaktır.
Bu sorunu aşmak için, Python bir dizi kontrol gerçekleştirir ve kontrollerden biri başarılı olursa sonucu döndürür. İki değerin işaretlerini karşılaştırır, ardından tamsayının bir kayan sayı için "çok büyük" olup olmadığını, ardından kayan öğenin üssünü tam sayı uzunluğuyla karşılaştırır. Tüm bu kontroller başarısız olursa, sonucu elde etmek için iki yeni Python nesnesi oluşturmak gerekir.
Bir şamandırayı vbir tamsayı / uzun ile karşılaştırırken w, en kötü durum şudur:
vve waynı işarete sahip (hem pozitif hem de negatif),
- tamsayı
w, size_ttipte tutulabilecek kadar az bite sahiptir (tipik olarak 32 veya 64 bit),
- tamsayıda
wen az 49 bit vardır,
- şamandıranın üssü, içindeki
vbit sayısı ile aynıdır w.
Ve bu, sorudaki değerler için tam olarak sahip olduğumuz şeydir:
>>> import math
>>> math.frexp(562949953420000.7) # gives the float's (significand, exponent) pair
(0.9999999999976706, 49)
>>> (562949953421000).bit_length()
49
49'un hem şamandıranın üssü hem de tamsayıdaki bit sayısı olduğunu görüyoruz. Her iki sayı da pozitiftir ve bu nedenle yukarıdaki dört kriter karşılanmıştır.
Değerlerden birini daha büyük (veya daha küçük) olarak seçmek, tamsayının bit sayısını veya üs değerini değiştirebilir ve böylece Python, pahalı son kontrolü gerçekleştirmeden karşılaştırmanın sonucunu belirleyebilir.
Bu, dilin CPython uygulamasına özgüdür.
Daha ayrıntılı karşılaştırma
float_richcompareFonksiyon iki değer arasında karşılaştırma kolları vve w.
Aşağıda, işlevin gerçekleştirdiği denetimlerin adım adım açıklaması verilmiştir. Python kaynağındaki yorumlar, işlevin ne yaptığını anlamaya çalışırken aslında çok yararlıdır, bu yüzden onları ilgili yerlerde bıraktım. Bu kontrolleri cevabın altındaki bir listede de özetledim.
Ana fikir, Python nesnelerini vve wiki uygun C çiftine eşlemektir ive jbu daha sonra doğru sonucu vermek için kolayca karşılaştırılabilir. Hem Python 2 hem de Python 3 bunu yapmak için aynı fikirleri kullanır (eski sadece ayrı ayrı ele alır intve longtürler).
Yapılacak ilk şey v, kesinlikle bir Python şamandırası olup olmadığını kontrol etmek ve bir C çiftine eşlemektir i. Daha sonra fonksiyon waynı zamanda bir şamandıra olup olmadığına bakar ve onu bir C çiftiyle eşler j. Diğer tüm kontroller atlanabileceğinden, bu işlev için en iyi senaryodur. Fonksiyon aynı zamanda kontrol eder olmadığını görmek için vise infya nan:
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 biliyoruz ki wbu kontroller başarısız olursa , bir Python şamandırası değildir. Şimdi fonksiyon bir Python tamsayısı olup olmadığını kontrol ediyor. Bu durumda, en kolay test işaretini vve işaretini ayıklamaktır w( eğer pozitif 0ise sıfır -1ise geri dön 1). İşaretler farklıysa, karşılaştırmanın sonucunu döndürmek için gereken tüm bilgiler budur:
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;
}
}
Bu onay başarısız olursa, o zaman vve waynı işarete sahip.
Bir sonraki kontrol, tamsayıdaki bit sayısını sayar w. Çok fazla biti varsa, muhtemelen bir şamandıra olarak tutulamaz ve bu nedenle şamandıradan 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;
}
Öte yandan, tamsayı w48 veya daha az bite sahipse, güvenli bir şekilde C çiftine dönüşebilir jve karşılaştırılabilir:
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, bunun w49 veya daha fazla biti olduğunu biliyoruz . wPozitif bir tam sayı olarak davranmak uygun olacaktır , bu nedenle işareti ve karşılaştırma operatörünü gerektiği gibi değiştirin:
if (nbits <= 48) {
/* "Multiply both sides" by -1; this also swaps the
* comparator.
*/
i = -i;
op = _Py_SwappedOp[op];
}
Şimdi fonksiyon şamandıranın üssüne bakar. Bir şamandıranın anlamlı * 2 üssü olarak yazılabileceğini (işareti yoksayarak) ve anlamlılığın 0,5 ile 1 arasında bir sayıyı temsil ettiğini hatırlayın :
(void) frexp(i, &exponent);
if (exponent < 0 || (size_t)exponent < nbits) {
i = 1.0;
j = 2.0;
goto Compare;
}
Bu iki şeyi kontrol eder. Üs 0'dan küçükse, şamandıra 1'den küçüktür (ve herhangi bir tamsayıdan büyüklükte daha küçüktür). Üs az bit sayısına göre ise Veya, wo zaman buna sahip v < |w|significand * 2 beri üs 2 den aşağı nbits .
Bu iki kontrol başarısız olursa, fonksiyon üssün bit sayısından daha büyük olup olmadığına bakar w. Bu anlamlı * 2 üssünün 2 nbit'ten büyük olduğunu gösterir ve bu nedenle v > |w|:
if ((size_t)exponent > nbits) {
i = 2.0;
j = 1.0;
goto Compare;
}
Bu kontrol başarılı olmazsa, şamandıranın üssünün vtamsayıdaki bit sayısı ile aynı olduğunu biliyoruz w.
İki değerleri artık karşılaştırılabilir tek yolu iki yeni Python tamsayılar oluşturmaktır vve w. Fikir, kesirli kısmını atmak, vtamsayı kısmını ikiye katlamak ve sonra bir tane eklemek. wiki katına çıkar ve bu iki yeni Python nesnesi doğru dönüş değerini vermek için karşılaştırılabilir. Küçük değerlere sahip bir örnek kullanılması 4.65 < 4karşılaştırma ile belirlenir (2*4)+1 == 9 < 8 == (2*4)(yanlış döndürme).
{
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ısacası, Python'un bu yeni nesneleri oluştururken yapması gereken ek hata kontrolü ve çöp izleme özelliğini bıraktım. Söylemeye gerek yok, bu ek yük ekler ve soruda vurgulanan değerlerin neden diğerlerine göre önemli ölçüde daha yavaş olduğunu açıklar.
Karşılaştırma fonksiyonu tarafından gerçekleştirilen kontrollerin bir özeti.
Izin vvermek bir şamandıra ve C çift olarak döküm. Şimdi, eğer wbir şamandıra ise:
Olmadığını kontrol edin wolduğunu nanveya inf. Öyleyse, bu özel durumu türüne bağlı olarak ayrı ayrı ele alın w.
Değilse, karşılaştırmak vve wC çiftlerde olarak temsiller doğrudan.
Eğer wbir tam sayı ise:
Belirtileri Özü vve w. Eğer farklılarsa, o zaman biliyoruz vve wfarklıyız ve bu daha büyük bir değerdir.
( İşaretler aynıdır. ) wKayan noktalı olmak için çok fazla bit olup olmadığını kontrol edin (daha fazla size_t). Eğer öyleyse, wdaha büyüktür v.
w48 veya daha az bit olup olmadığını kontrol edin . Eğer öyleyse, hassasiyetini kaybetmeden güvenli bir şekilde C çiftine dökülebilir ve ile karşılaştırılabilir v.
( w48'den fazla biti var. Şimdi wkarşılaştırma op'unu uygun şekilde değiştiren pozitif bir tamsayı olarak ele alacağız . )
Şamandıranın üssünü düşünün v. Üs negatifse, o zaman herhangi bir pozitif tamsayıdan vküçüktür 1ve bu nedenle küçüktür. Aksi takdirde, üs bit içindeki sayıdan azsa, wo zaman bu sayıdan az olmalıdır w.
Üssü vbit sayısından büyükse wo zaman vbüyüktür w.
( Üs, içindeki bit sayısı ile aynıdır w. )
Son kontrol. Bölünmüş vonun tamsayı ve kesirli bölüme. Tamsayı kısmını ikiye katlayın ve kesirli kısmı telafi etmek için 1 ekleyin. Şimdi tamsayıyı iki katına çıkarın w. Sonuç almak için bu iki yeni tamsayıyı karşılaştırın.