Арифметика с произвольно большими целыми числами в PHP

Итак, PHP - не лучший язык для работы с произвольно большими целыми числами, учитывая, что он изначально поддерживает только 32-битные целые числа со знаком. Однако я пытаюсь создать класс, который мог бы представлять произвольно большое двоичное число и иметь возможность выполнять простые арифметические операции с двумя из них (сложение / вычитание / умножение / деление).

Моя цель имеет дело со 128-битными целыми числами.

Я рассматриваю несколько подходов и вижу в них проблемы. Мы будем очень признательны за любой вклад или комментарий о том, что вы бы выбрали и как вы могли бы это сделать.

Подход №1: Создайте 128-битный целочисленный класс, который хранит свое целое число внутри как четыре 32-битных целых числа. Единственная проблема с этим подходом заключается в том, что я не уверен, как решать проблемы переполнения / потери значимости при манипулировании отдельными фрагментами двух операндов.

Подход №2: Используйте расширение bcmath, так как это похоже на то, для чего оно было разработано. Единственное, что меня беспокоит, когда я использую этот подход, - это настройка масштаба расширения bcmath, потому что в моих 128-битных целых числах не может быть ошибок округления; они должны быть точными. Я также беспокоюсь о возможности в конечном итоге преобразовать результат функций bcmath в двоичную строку (которую мне позже нужно будет вставить в некоторые функции шифрования mcrypt).

Подход № 3: Сохраняет числа как двоичные строки (возможно, сначала младший бит). Теоретически я должен иметь возможность хранить таким образом целые числа любого произвольного размера. Все, что мне нужно было сделать, это написать четыре основные арифметические функции для выполнения add / sub / mult / div для двух двоичных строк и получения результата в виде двоичной строки. Это именно тот формат, который мне нужно передать в mcrypt, так что это дополнительный плюс. Это подход, который, на мой взгляд, наиболее перспективен на данный момент, но у меня есть одна проблема, заключающаяся в том, что PHP не предлагает мне никакого способа манипулировать отдельными битами (о которых я знаю). Я считаю, что мне пришлось бы разбить его на куски размером в байты (без каламбура), и тогда мои вопросы об обработке переполнения / потери значимости из Подхода № 1 применимы.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
8
0
4 167
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Для этого уже существуют различные классыимеется в наличии, поэтому вы можете посмотреть на них, прежде чем писать собственное решение (если действительно написание собственного решения все еще необходимо).

Насколько я могу судить, расширение bcmath - это то, что вам нужно. Данные в руководстве по PHP немного скудны, но вы можете установить точность, которая будет именно той, которая вам нужна, с помощью функции bcscale () или необязательного третьего параметра в большинстве других функций bcmath. Не слишком уверен в том, что касается двоичных строк, но немного погуглил, и я понял, что вы должны справиться с этим, используя функцию pack ().

Ответ принят как подходящий

Для этого подойдет Расширение PHP GMP. В качестве дополнительного бонуса вы можете использовать его для преобразования десятичных чисел в двоичные, например:

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

Спасибо, похоже, очень красивое расширение!

Bob Somers 05.09.2009 09:01

Если вы используете PHP x64 в Windows, вам не повезло с расширением GMP. Похоже, что разработчик библиотеки GMP не перенес ее на Windows x64, хотя она доступна для Linux x64. Чтобы обойти это, вы можете использовать MySQL, если вы используете эту платформу базы данных в своем проекте. MySQL поддерживает 64-битные целые числа и имеет множество логических операторов и базовую функцию преобразования. Я написал простой класс, чтобы облегчить это в моем проекте.

Jonathon Hill 11.10.2009 06:43

Я реализовал следующий Специалист по оценке жалоб PEMDAS BC, который может быть вам полезен.

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

Он автоматически обрабатывает ошибки округления, просто установите точность до тех цифр, которые вам нужны.

Другие вопросы по теме