फ्लोट ऑब्जेक्ट्स के लिए पायथन स्रोत कोड में एक टिप्पणी स्वीकार करती है कि:
तुलना बहुत बुरा सपना है
फ्लोट की तुलना पूर्णांक से करते समय यह विशेष रूप से सच है, क्योंकि, फ्लोट के विपरीत, पायथन में पूर्णांक मनमाने ढंग से बड़े हो सकते हैं और हमेशा सटीक होते हैं। पूर्णांक को फ्लोट में डालने की कोशिश करने से सटीकता खो सकती है और तुलना गलत हो सकती है। फ्लोट को एक पूर्णांक में डालने की कोशिश करना या तो काम करने वाला नहीं है क्योंकि कोई भी आंशिक हिस्सा खो जाएगा।
इस समस्या को हल करने के लिए, पायथन चेक की एक श्रृंखला करता है, यदि चेक में से कोई एक सफल होता है, तो परिणाम लौटाता है। यह दो मानों के संकेतों की तुलना करता है, फिर पूर्णांक "बहुत बड़ा" है, जो फ्लोट होने के लिए है, फिर फ्लोट के घातांक की तुलना पूर्णांक से करता है। यदि ये सभी चेक विफल हो जाते हैं, तो परिणाम प्राप्त करने के लिए तुलना करने के लिए दो नए पायथन ऑब्जेक्ट्स का निर्माण करना आवश्यक है।
जब एक फ्लोट की तुलना किसी v
पूर्णांक / लंबे समय तक की जाती है w
, तो सबसे खराब स्थिति यह होती है:
v
और w
एक ही संकेत है (सकारात्मक या नकारात्मक दोनों),
- पूर्णांक
w
में कुछ पर्याप्त बिट्स होते हैं जिन्हें यह size_t
प्रकार में रखा जा सकता है (आमतौर पर 32 या 64 बिट्स),
- पूर्णांक
w
में कम से कम 49 बिट्स हैं,
- फ्लोट
v
का प्रतिपादक बिट्स की संख्या के समान है w
।
और यह वही है जो हमारे पास सवाल में मूल्यों के लिए है:
>>> import math
>>> math.frexp(562949953420000.7) # gives the float's (significand, exponent) pair
(0.9999999999976706, 49)
>>> (562949953421000).bit_length()
49
हम देखते हैं कि पूर्णांक में फ्लोट का एक्सपोनेंट और बिट्स की संख्या दोनों हैं। दोनों संख्याएं सकारात्मक हैं और इसलिए उपरोक्त चार मानदंड पूरे किए गए हैं।
मूल्यों में से एक को बड़ा (या छोटा) चुनना पूर्णांक के बिट्स की संख्या, या घातांक के मूल्य को बदल सकता है, और इसलिए पायथन महंगी अंतिम जांच किए बिना तुलना के परिणाम को निर्धारित करने में सक्षम है।
यह भाषा के CPython कार्यान्वयन के लिए विशिष्ट है।
अधिक विस्तार से तुलना
float_richcompare
समारोह दो मानों के बीच तुलना को संभालती है v
और w
।
नीचे फ़ंक्शन द्वारा निष्पादित चेक का चरण-दर-चरण वर्णन है। पायथन स्रोत में टिप्पणियां वास्तव में बहुत उपयोगी होती हैं, जब यह समझने की कोशिश की जाती है कि फ़ंक्शन क्या करता है, इसलिए मैंने उन्हें प्रासंगिक में छोड़ दिया है। मैंने उत्तर के चरणों में एक सूची में इन चेकों को संक्षेप में प्रस्तुत किया है।
मुख्य विचार पायथन ऑब्जेक्ट्स को मैप करना v
और w
दो उचित सी डबल्स के लिए है, i
और j
, जिसके बाद सही परिणाम देने के लिए आसानी से तुलना की जा सकती है। पाइथन 2 और पाइथन 3 दोनों ही ऐसा करने के लिए समान विचारों का उपयोग करते हैं (पूर्व सिर्फ हैंडल int
और long
प्रकार अलग से)।
पहली बात यह है कि v
निश्चित रूप से पायथन फ्लोट है और इसे सी डबल में मैप करें i
। अगला फ़ंक्शन यह देखता है कि क्या w
यह भी फ्लोट है और इसे सी डबल में मैप करता है j
। यह फ़ंक्शन के लिए सबसे अच्छा मामला है क्योंकि अन्य सभी चेक को छोड़ दिया जा सकता है। फ़ंक्शन यह देखने के लिए भी जांच करता है कि क्या v
है inf
या 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;
}
अब हम जानते हैं कि यदि w
ये जाँच विफल हो गई, तो यह पायथन फ्लोट नहीं है। अब फ़ंक्शन जाँचता है कि क्या यह पायथन पूर्णांक है। यदि यह मामला है, तो सबसे आसान परीक्षा है साइन इन करना v
और साइन इन करना w
(वापसी 0
शून्य, -1
यदि ऋणात्मक, 1
यदि सकारात्मक)। यदि संकेत भिन्न हैं, तो तुलना के परिणाम को वापस करने के लिए आवश्यक सभी जानकारी है:
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;
}
}
यदि यह जांच विफल हो गई, तो v
और w
उसी पर हस्ताक्षर करें।
अगला चेक पूर्णांक में बिट्स की संख्या को गिनता है w
। यदि इसमें बहुत अधिक बिट्स हैं तो इसे संभवतः फ्लोट के रूप में नहीं रखा जा सकता है और यह फ्लोट की तुलना में परिमाण में बड़ा होना चाहिए 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;
}
दूसरी ओर, यदि पूर्णांक w
में 48 या उससे कम बिट्स हैं, तो यह सुरक्षित रूप से C डबल j
और तुलना में बदल सकता है :
if (nbits <= 48) {
j = PyLong_AsDouble(w);
/* It's impossible that <= 48 bits overflowed. */
assert(j != -1.0 || ! PyErr_Occurred());
goto Compare;
}
इस बिंदु से, हम जानते हैं कि w
49 या अधिक बिट्स हैं। w
सकारात्मक पूर्णांक के रूप में व्यवहार करना सुविधाजनक होगा , इसलिए साइन और तुलना ऑपरेटर को आवश्यक रूप से बदलें:
if (nbits <= 48) {
/* "Multiply both sides" by -1; this also swaps the
* comparator.
*/
i = -i;
op = _Py_SwappedOp[op];
}
अब फ़ंक्शन फ्लोट के घातांक को देखता है। स्मरण करो कि एक फ्लोट लिखा जा सकता है (संकेत को अनदेखा करते हुए) महत्व के रूप में * 2 घातांक और यह कि महत्व 0.5 / 1 के बीच एक संख्या का प्रतिनिधित्व करता है:
(void) frexp(i, &exponent);
if (exponent < 0 || (size_t)exponent < nbits) {
i = 1.0;
j = 2.0;
goto Compare;
}
यह दो चीजों की जाँच करता है। यदि प्रतिपादक 0 से कम है तो फ्लोट 1 से छोटा है (और किसी भी पूर्णांक की तुलना में परिमाण में छोटा है)। या, यदि प्रतिपादक बिट्स की संख्या से कम है, w
तो हमारे पास यह है कि v < |w|
महत्व * 2 घातांक 2 nbit से कम है ।
इन दो जांचों को विफल करते हुए, फ़ंक्शन यह देखता है कि एक्सपोनेंट बिट की संख्या से अधिक है या नहीं w
। यह दिखाता है कि significand * 2 प्रतिपादक 2 से अधिक है nbits और इतने v > |w|
:
if ((size_t)exponent > nbits) {
i = 2.0;
j = 1.0;
goto Compare;
}
यदि यह जांच सफल नहीं हुई, तो हम जानते हैं कि फ्लोट v
का घातांक पूर्णांक में बिट्स की संख्या के समान है w
।
एकमात्र तरीका यह है कि अब दो मूल्यों की तुलना की जा सकती है v
और इसमें से दो नए पायथन पूर्णांक का निर्माण करना है w
। विचार यह है कि आंशिक भाग को छोड़ दें v
, पूर्णांक भाग को दोगुना करें, और फिर एक जोड़ें। w
को दोगुना भी किया जाता है और इन दो नए पायथन ऑब्जेक्ट्स की तुलना सही रिटर्न वैल्यू देने के लिए की जा सकती है। छोटे मूल्यों के साथ एक उदाहरण का उपयोग करना, 4.65 < 4
तुलना (2*4)+1 == 9 < 8 == (2*4)
(झूठे वापस करने) द्वारा निर्धारित किया जाएगा ।
{
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
}
}
संक्षिप्तता के लिए मैंने अतिरिक्त त्रुटि-जांच और कचरा-ट्रैकिंग पायथन को छोड़ दिया है, जब यह इन नई वस्तुओं को बनाता है। कहने की जरूरत नहीं है, यह अतिरिक्त उपरि जोड़ता है और बताता है कि क्यों सवाल में हाइलाइट किए गए मान दूसरों की तुलना में काफी धीमा हैं।
यहां उन चेकों का सारांश दिया गया है जो तुलनात्मक कार्य द्वारा किए जाते हैं।
चलो v
एक फ्लोट बनें और इसे सी डबल के रूप में कास्ट करें। अब, अगर w
यह भी एक नाव है:
जाँच करें कि क्या w
है nan
या inf
। यदि हां, तो इस विशेष मामले को अलग प्रकार के आधार पर संभालें w
।
यदि नहीं, तो सी डबल्स के रूप में उनके अभ्यावेदन से तुलना करें v
और w
सीधे।
यदि w
एक पूर्णांक है:
के संकेत निकालें v
और w
। यदि वे भिन्न हैं तो हम जानते हैं v
और w
भिन्न हैं और जो अधिक मूल्य है।
( संकेत समान हैं। ) जांचें कि क्या w
कई बिट्स एक फ्लोट (अधिक से अधिक size_t
) होने के लिए हैं। यदि ऐसा है, w
की तुलना में अधिक परिमाण है v
।
जाँच करें कि w
क्या 48 या उससे कम बिट्स हैं। यदि हां, तो इसे अपनी सटीकता खोए बिना और तुलना के साथ सी डबल में सुरक्षित रूप से डाला जा सकता है v
।
( w
48 से अधिक बिट्स हैं। अब हम w
एक सकारात्मक पूर्णांक के रूप में व्यवहार करेंगे, जिसमें तुलनात्मक ऑप्स को उपयुक्त रूप से बदल दिया जाएगा। )
फ्लोट के घातांक पर विचार करें v
। यदि घातांक नकारात्मक है, तो किसी भी सकारात्मक पूर्णांक से v
कम है 1
और इसलिए कम है। और, यदि प्रतिपादक बिट्स की संख्या से कम है w
तो यह उससे कम होना चाहिए w
।
यदि का प्रतिपादक v
बिट्स की संख्या से अधिक है w
तो v
की तुलना में अधिक है w
।
( प्रतिपादक बिट की संख्या के समान है w
। )
अंतिम जाँच। v
इसके पूर्णांक और भिन्नात्मक भागों में विभाजित करें । पूर्णांक भाग को दोगुना करें और आंशिक भाग की भरपाई के लिए 1 जोड़ें। अब पूर्णांक को दोगुना करें w
। परिणाम प्राप्त करने के बजाय इन दो नए पूर्णांकों की तुलना करें।