الحساب مع الأعداد الصحيحة الكبيرة بشكل تعسفي في PHP

StackOverflow https://stackoverflow.com/questions/37391

  •  09-06-2019
  •  | 
  •  

سؤال

حسنًا، PHP ليست أفضل لغة للتعامل مع الأعداد الصحيحة الكبيرة بشكل تعسفي، مع الأخذ في الاعتبار أنها تدعم فقط الأعداد الصحيحة الموقعة 32 بت.ما أحاول القيام به هو إنشاء فئة يمكن أن تمثل رقمًا ثنائيًا كبيرًا بشكل تعسفي وتكون قادرة على إجراء عمليات حسابية بسيطة على اثنين منهم (إضافة/طرح/ضرب/قسمة).

هدفي هو التعامل مع الأعداد الصحيحة 128 بت.

هناك طريقتان أنظر إليهما، والمشاكل التي أراها معهم.أي مساهمة أو تعليق حول ما ستختاره وكيف يمكنك القيام به سيكون موضع تقدير كبير.

النهج رقم 1: قم بإنشاء فئة عدد صحيح 128 بت تقوم بتخزين عددها الصحيح داخليًا كأربعة أعداد صحيحة 32 بت.المشكلة الوحيدة في هذا الأسلوب هي أنني لست متأكدًا من كيفية التعامل مع مشكلات التجاوز/التجاوز عند التعامل مع الأجزاء الفردية من المعاملين.

النهج رقم 2: استخدم ملحق bcmath، حيث يبدو أن هذا شيء تم تصميمه لمعالجته.قلقي الوحيد في اتباع هذا النهج هو إعداد مقياس ملحق bcmath، لأنه لا يمكن أن يكون هناك أي أخطاء تقريب في الأعداد الصحيحة ذات 128 بت؛يجب أن تكون دقيقة.أنا قلق أيضًا بشأن القدرة على تحويل نتيجة وظائف bcmath في النهاية إلى سلسلة ثنائية (والتي سأحتاج لاحقًا إلى إدخالها في بعض وظائف تشفير mcrypt).

النهج رقم 3: قم بتخزين الأرقام كسلاسل ثنائية (ربما LSB أولاً).من الناحية النظرية يجب أن أكون قادرًا على تخزين الأعداد الصحيحة بأي حجم عشوائي بهذه الطريقة.كل ما علي فعله هو كتابة الدوال الحسابية الأربع الأساسية لتنفيذ add/sub/mult/div على سلسلتين ثنائيتين وإنتاج نتيجة سلسلة ثنائية.هذا هو بالضبط التنسيق الذي أحتاج إلى تسليمه إلى mcrypt أيضًا، لذا فهذه ميزة إضافية.أعتقد أن هذا هو النهج الأكثر وعدًا في الوقت الحالي، ولكن النقطة الشائكة الوحيدة التي لدي هي أن PHP لا تقدم لي أي طريقة للتعامل مع البتات الفردية (التي أعرفها).أعتقد أنه سيتعين علي تقسيمها إلى أجزاء بحجم البايت (لا يقصد التورية)، وعند هذه النقطة تنطبق أسئلتي حول التعامل مع السعة الفائضة/التدفق الناقص من النهج رقم 1.

هل كانت مفيدة؟

المحلول

ال امتداد PHP GMP سيكون أفضل لهذا.كمكافأة إضافية، يمكنك استخدامها لإجراء التحويل من الرقم العشري إلى الثنائي، كما يلي:

gmp_strval(gmp_init($n, 10), 2);

نصائح أخرى

هناك بالفعل مختلف الطبقات متاح لهذا قد ترغب في إلقاء نظرة عليها قبل كتابة الحل الخاص بك (إذا كانت كتابة الحل الخاص بك لا تزال مطلوبة بالفعل).

وبقدر ما أستطيع أن أقول، فإن امتداد bcmath هو الذي تريده.البيانات الموجودة في دليل PHP متفرقة بعض الشيء، ولكن يمكنك ضبط الدقة لتكون بالضبط ما تحتاجه باستخدام الدالة bcscale()، أو المعلمة الثالثة الاختيارية في معظم وظائف bcmath الأخرى.لست متأكدًا تمامًا من مسألة السلاسل الثنائية، ولكن القليل من البحث على Google يخبرني أنه يجب أن تكون قادرًا على القيام بذلك من خلال استخدام وظيفة pack().

لقد نفذت ما يلي مقيم شكوى PEMDAS قبل الميلاد والتي قد تكون مفيدة لك.

function BC($string, $precision = 32)
{
    if (extension_loaded('bcmath') === true)
    {
        if (is_array($string) === true)
        {
            if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
            {
                $callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');

                if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
                {
                    $x = 1;
                    $result = @call_user_func_array('bc' . $callback[$operator], $string);

                    if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
                    {
                        $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));

                        do
                        {
                            $x = $y;
                            $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
                        }

                        while (BC(sprintf('%s > %s', $x, $y)));
                    }

                    if (strpos($result = bcmul($x, $result), '.') !== false)
                    {
                        $result = rtrim(rtrim($result, '0'), '.');

                        if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
                        {
                            $result = bcadd($result, (strncmp('-', $result, 1) === 0) ? -1 : 1, 0);
                        }

                        else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
                        {
                            $result = bcmul($result, 1, 0);
                        }
                    }

                    return $result;
                }

                return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
            }

            $string = array_shift($string);
        }

        $string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));

        while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
        {
            $string = preg_replace_callback('~[(]([^()]++)[)]~', __FUNCTION__, $string);
        }

        foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
        {
            while (preg_match(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), $string) > 0)
            {
                $string = preg_replace_callback(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), __FUNCTION__, $string, 1);
            }
        }
    }

    return (preg_match('~^[+-]?[0-9]++(?:[.][0-9]++)?$~', $string) > 0) ? $string : false;
}

فهو يتعامل تلقائيًا مع أخطاء التقريب، ما عليك سوى ضبط الدقة على أي رقم تحتاجه.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top