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






Для этого уже существуют различные классыимеется в наличии, поэтому вы можете посмотреть на них, прежде чем писать собственное решение (если действительно написание собственного решения все еще необходимо).
Насколько я могу судить, расширение bcmath - это то, что вам нужно. Данные в руководстве по PHP немного скудны, но вы можете установить точность, которая будет именно той, которая вам нужна, с помощью функции bcscale () или необязательного третьего параметра в большинстве других функций bcmath. Не слишком уверен в том, что касается двоичных строк, но немного погуглил, и я понял, что вы должны справиться с этим, используя функцию pack ().
Для этого подойдет Расширение PHP GMP. В качестве дополнительного бонуса вы можете использовать его для преобразования десятичных чисел в двоичные, например:
gmp_strval(gmp_init($n, 10), 2);
Если вы используете PHP x64 в Windows, вам не повезло с расширением GMP. Похоже, что разработчик библиотеки GMP не перенес ее на Windows x64, хотя она доступна для Linux x64. Чтобы обойти это, вы можете использовать MySQL, если вы используете эту платформу базы данных в своем проекте. MySQL поддерживает 64-битные целые числа и имеет множество логических операторов и базовую функцию преобразования. Я написал простой класс, чтобы облегчить это в моем проекте.
Я реализовал следующий Специалист по оценке жалоб 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;
}
Он автоматически обрабатывает ошибки округления, просто установите точность до тех цифр, которые вам нужны.
Спасибо, похоже, очень красивое расширение!