কিছু ভাসমান <পূর্ণসংখ্যার তুলনা অন্যদের চেয়ে চারগুণ ধীর কেন হয়?


284

পূর্ণসংখ্যার সাথে ভাসমানের তুলনা করার সময়, কিছু জোড় মান একই মানের মাত্রার অন্যান্য মানের তুলনায় মূল্যায়ন করতে অনেক বেশি সময় নেয়।

উদাহরণ স্বরূপ:

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

তবে যদি ভাসা বা পূর্ণসংখ্যাকে একটি নির্দিষ্ট পরিমাণ দ্বারা আরও ছোট বা বড় করা হয় তবে তুলনাটি আরও দ্রুত চলে:

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

তুলনা অপারেটর (যেমন ব্যবহার ==বা >পরিবর্তে) পরিবর্তন করা সময়কে কোনও লক্ষণীয় উপায়ে প্রভাবিত করে না।

এটি সম্পূর্ণরূপে মাত্রার সাথে সম্পর্কিত নয় কারণ বৃহত্তর বা ছোট মানগুলি বাছাইয়ের ফলে দ্রুত তুলনা হতে পারে, সুতরাং আমার সন্দেহ হয় যে এটি কিছুটা দুর্ভাগ্যজনক উপায়ে বিটস লাইন আপের নিচে রয়েছে।

স্পষ্টতই, এই মানগুলির তুলনা বেশিরভাগ ব্যবহারের ক্ষেত্রে যথেষ্ট দ্রুতগতির। আমি কেবল কৌতূহল বোধ করি পাইথন কেন অন্যের চেয়ে কিছু জোড়া মান নিয়ে বেশি লড়াই করে বলে মনে হয়।


এটি কি 2.7 এবং 3.x উভয় ক্ষেত্রে একই?
thefourtheye

উপরের সময়গুলি পাইথন ৩.৪ থেকে প্রাপ্ত - আমার লিনাক্স কম্পিউটারে ২. running চলমান সময়গুলির মধ্যে একই রকম তাত্পর্য ছিল (৩ থেকে ৪ এবং একটি বিট বার ধীর হয়ে)।
অ্যালেক্স রিলি

1
আকর্ষণীয় লেখার জন্য ধন্যবাদ। আমি কী প্রশ্নটি অনুপ্রাণিত করেছিলাম তা জানতে আগ্রহী - আপনি কি এলোমেলোভাবে তুলনার সময় নির্ধারণ করেছিলেন বা এর পিছনে কোনও গল্প আছে?
Veedrac

3
@ ভিড্রাক: আপনাকে ধন্যবাদ গল্পের খুব বেশি কিছু নেই: আমি অযৌক্তিকভাবে-মনের ভাব নিয়ে ভাবছিলাম যে কত দ্রুত ভাসমান এবং পূর্ণসংখ্যার তুলনা করা হয়, কয়েকটি মান নির্ধারিত হয় এবং কিছু ছোটখাটো পার্থক্য লক্ষ্য করা যায়। তখন আমি বুঝতে পেরেছিলাম যে পাইথন কীভাবে সঠিকভাবে ভাসমান এবং বড় সংখ্যার তুলনা করতে পরিচালিত হয়েছিল সে সম্পর্কে আমার কোনও ধারণা নেই। উত্সটি বোঝার চেষ্টা করে আমি কিছুটা সময় ব্যয় করেছি এবং জেনেছি সবচেয়ে খারাপ পরিস্থিতি কী।
অ্যালেক্স রিলি

2
@ ইয়ভেসডাউস্ট: এই বিশেষ মানগুলি নয়, না (এটি অবিশ্বাস্য ভাগ্য হত!)। আমি বিভিন্ন যুগের মানগুলি চেষ্টা করেছি এবং সময়গুলির মধ্যে ছোট পার্থক্য লক্ষ্য করেছি (উদাহরণস্বরূপ খুব বড় পূর্ণসংখ্যার তুলনায় একটি সামান্য দৈর্ঘ্যের একটি ফ্লোটের তুলনা)। তুলনাটি কীভাবে কাজ করেছে তা বোঝার জন্য উত্সটি দেখার পরে আমি 2 case 49 কেস সম্পর্কে শিখেছি। আমি প্রশ্নের মানগুলি বেছে নিয়েছি কারণ তারা বিষয়টিকে সবচেয়ে জোরালোভাবে উপস্থাপন করেছে।
অ্যালেক্স রিলি

উত্তর:


354

ভাসমান বস্তুর জন্য পাইথন উত্স কোডের একটি মন্তব্য স্বীকার করে যে:

তুলনা করা অনেকটা দুঃস্বপ্ন

কোনও পূর্ণসংখ্যার সাথে একটি ফ্লোটের তুলনা করার ক্ষেত্রে এটি বিশেষভাবে সত্য, কারণ, ফ্লোটের বিপরীতে পাইথনের পূর্ণসংখ্যা নির্বিচারে বড় হতে পারে এবং সর্বদা নির্ভুল হয়। পূর্ণসংখ্যাকে একটি ফ্লোটে কাস্ট করার চেষ্টা করা নির্ভুলতা হারাতে এবং তুলনাটি ভুল করে তুলতে পারে। কোনও পূর্ণসংখ্যার সাথে ভাসাটি কাস্ট করার চেষ্টা করা হয় না কারণ কোনও ভগ্নাংশের অংশ নষ্ট হবে।

এই সমস্যাটি পেতে, পাইথন একটি চেক সিরিজ করে, যদি কোনও চেক সফল হয় তবে ফলাফলটি ফিরিয়ে দেয় returning এটি দুটি মানের চিহ্নগুলির সাথে তুলনা করে, তারপরে পূর্ণসংখ্যাটি "ভীষণ বড়" কিনা ভাসমান হতে হবে, তারপরে ভাসমানটির ঘনকটিকে পূর্ণসংখ্যার দৈর্ঘ্যের সাথে তুলনা করে। যদি এই সমস্ত চেক ব্যর্থ হয় তবে ফলাফলটি পেতে তুলনামূলকভাবে দুটি নতুন পাইথন অবজেক্ট তৈরি করা প্রয়োজন।

যখন 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

আমরা দেখতে পাই যে 49 হ'ল ভাসমান এবং তার পূর্ণসংখ্যার বিটের সংখ্যা উভয়ই। উভয় সংখ্যা ইতিবাচক এবং তাই উপরের চারটি মানদণ্ড পূরণ করা হয়েছে।

মানগুলির মধ্যে একটিকে বৃহত্তর (বা আরও ছোট) বাছাই করা পূর্ণসংখ্যার বিটের সংখ্যা বা ব্যয়কারীর মান পরিবর্তন করতে পারে এবং তাই পাইথন ব্যয়বহুল চূড়ান্ত চেক না করে তুলনার ফলাফল নির্ধারণ করতে সক্ষম হয়।

এটি ভাষার সিপিথন বাস্তবায়নের জন্য নির্দিষ্ট।


আরও বিস্তারিতভাবে তুলনা

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;
    }

অন্যদিকে, যদি পূর্ণসংখ্যার w48 বা তার কম বিট থাকে তবে এটি নিরাপদে সি ডাবলে পরিণত হতে পারে jএবং তুলনা করতে পারে :

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

এই দিক থেকে, আমরা জানি যে w49 বা তার বেশি বিট রয়েছে has এটি 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 নবিট কম

এই দুটি চেক ব্যর্থ হয়ে, ফাংশনটি বিট সংখ্যার চেয়ে এক্সপোনেন্টটি বেশি কিনা তা দেখতে দেখায় 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

  • w48 বা কম বিট আছে কিনা তা পরীক্ষা করুন । যদি তা হয় তবে এটি নির্ভুলতা না হারিয়ে নিরাপদে সি ডাবলে ফেলে দেওয়া যায় এবং এর সাথে তুলনা করা যায় v

  • ( w48 টিরও বেশি বিট রয়েছে। এখন wতুলনামূলক বিকল্পটি যথাযথ হিসাবে পরিবর্তন করে আমরা ইতিবাচক পূর্ণসংখ্যার হিসাবে বিবেচনা করব ))

  • ভাসমানটির উদ্দীপক বিবেচনা করুন v। তাহলে এক্সপোনেন্ট নেতিবাচক হয়, তাহলে vকম 1কোনো ধনাত্মক পূর্ণসংখ্যা চেয়ে এবং সেইজন্য কম। অন্যথায়, যদি বিট সংখ্যার তুলনায় বেদীটি কম হয় wতবে অবশ্যই এটির চেয়ে কম হওয়া উচিত w

  • তাহলে এর এক্সপোনেন্ট vবিট সংখ্যার চেয়ে বেশী wতারপর vচেয়ে বেশী w

  • ( এক্সপোনেন্ট বিট সংখ্যা হিসাবে একই w )

  • চূড়ান্ত চেক। vএর পূর্ণসংখ্যা এবং ভগ্নাংশের মধ্যে বিভক্ত করুন । ভগ্নাংশের অংশটি ক্ষতিপূরণ করতে পূর্ণসংখ্যার অংশটি দ্বিগুণ করুন এবং 1 যুক্ত করুন। এখন পূর্ণসংখ্যা দ্বিগুণ করুন w। ফলাফল পাওয়ার পরিবর্তে এই দুটি নতুন পূর্ণসংখ্যার তুলনা করুন।


4
ভাল কাজ পাইথন বিকাশকারী - বেশিরভাগ ভাষা বাস্তবায়ন কেবলমাত্র ভাসা / পূর্ণসংখ্যার তুলনাটি সঠিক নয় বলে এই বিষয়টিকে হস্তান্তরিত করে।
ব্যবহারকারী 253751

4

ব্যবহার gmpy2নির্বিচারে স্পষ্টতা ভাসে এবং পূর্ণসংখ্যার সঙ্গে এটা সম্ভব বেশি অভিন্ন তুলনা কর্মক্ষমতা পেতে:

~ $ ptipython
Python 3.5.1 |Anaconda 4.0.0 (64-bit)| (default, Dec  7 2015, 11:16:01) 
Type "copyright", "credits" or "license" for more information.

IPython 4.1.2 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: import gmpy2

In [2]: from gmpy2 import mpfr

In [3]: from gmpy2 import mpz

In [4]: gmpy2.get_context().precision=200

In [5]: i1=562949953421000

In [6]: i2=562949953422000

In [7]: f=562949953420000.7

In [8]: i11=mpz('562949953421000')

In [9]: i12=mpz('562949953422000')

In [10]: f1=mpfr('562949953420000.7')

In [11]: f<i1
Out[11]: True

In [12]: f<i2
Out[12]: True

In [13]: f1<i11
Out[13]: True

In [14]: f1<i12
Out[14]: True

In [15]: %timeit f<i1
The slowest run took 10.15 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 441 ns per loop

In [16]: %timeit f<i2
The slowest run took 12.55 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 152 ns per loop

In [17]: %timeit f1<i11
The slowest run took 32.04 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 269 ns per loop

In [18]: %timeit f1<i12
The slowest run took 36.81 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 231 ns per loop

In [19]: %timeit f<i11
The slowest run took 78.26 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 156 ns per loop

In [20]: %timeit f<i12
The slowest run took 21.24 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 194 ns per loop

In [21]: %timeit f1<i1
The slowest run took 37.61 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 275 ns per loop

In [22]: %timeit f1<i2
The slowest run took 39.03 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 259 ns per loop

1
আমি এই গ্রন্থাগারটি এখনও ব্যবহার করি নি, তবে এটি সম্ভাব্যভাবে খুব দরকারী বলে মনে হচ্ছে। ধন্যবাদ!
অ্যালেক্স রিলে

এটি সিম্পি এবং এমপিমেথ দ্বারা ব্যবহৃত হয়
ডেনফ্রুমুফা

CPython এছাড়াও আছে decimalমান লাইব্রেরিতে
denfromufa
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.