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ı v
bir tamsayı / uzun ile karşılaştırırken w
, en kötü durum şudur:
v
ve w
aynı işarete sahip (hem pozitif hem de negatif),
- tamsayı
w
, size_t
tipte tutulabilecek kadar az bite sahiptir (tipik olarak 32 veya 64 bit),
- tamsayıda
w
en az 49 bit vardır,
- şamandıranın üssü, içindeki
v
bit 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_richcompare
Fonksiyon iki değer arasında karşılaştırma kolları v
ve 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 v
ve w
iki uygun C çiftine eşlemektir i
ve j
bu 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 int
ve long
tü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 w
aynı 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 v
ise inf
ya 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 w
bu 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 v
ve işaretini ayıklamaktır w
( eğer pozitif 0
ise sıfır -1
ise 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 v
ve w
aynı 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ı w
48 veya daha az bite sahipse, güvenli bir şekilde C çiftine dönüşebilir j
ve 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 w
49 veya daha fazla biti olduğunu biliyoruz . w
Pozitif 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, w
o 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 v
tamsayı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 v
ve w
. Fikir, kesirli kısmını atmak, v
tamsayı kısmını ikiye katlamak ve sonra bir tane eklemek. w
iki 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 < 4
karşı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 v
vermek bir şamandıra ve C çift olarak döküm. Şimdi, eğer w
bir şamandıra ise:
Olmadığını kontrol edin w
olduğunu nan
veya inf
. Öyleyse, bu özel durumu türüne bağlı olarak ayrı ayrı ele alın w
.
Değilse, karşılaştırmak v
ve w
C çiftlerde olarak temsiller doğrudan.
Eğer w
bir tam sayı ise:
Belirtileri Özü v
ve w
. Eğer farklılarsa, o zaman biliyoruz v
ve w
farklıyız ve bu daha büyük bir değerdir.
( İşaretler aynıdır. ) w
Kayan noktalı olmak için çok fazla bit olup olmadığını kontrol edin (daha fazla size_t
). Eğer öyleyse, w
daha büyüktür v
.
w
48 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
.
( w
48'den fazla biti var. Şimdi w
karşı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 v
küçüktür 1
ve bu nedenle küçüktür. Aksi takdirde, üs bit içindeki sayıdan azsa, w
o zaman bu sayıdan az olmalıdır w
.
Üssü v
bit sayısından büyükse w
o zaman v
büyüktür w
.
( Üs, içindeki bit sayısı ile aynıdır w
. )
Son kontrol. Bölünmüş v
onun 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.