PHP中任意大整数的算术运算
Posted
技术标签:
【中文标题】PHP中任意大整数的算术运算【英文标题】:Arithmetic with Arbitrarily Large Integers in PHP 【发布时间】:2010-09-07 10:25:02 【问题描述】:好的,所以 php 不是处理任意大整数的最佳语言,因为它本身只支持 32 位有符号整数。我想做的是创建一个可以表示任意大二进制数并能够对其中两个执行简单算术运算(加/减/乘/除)的类。
我的目标是处理 128 位整数。
我正在研究几种方法,并发现它们存在的问题。非常感谢您对您将选择什么以及如何进行的任何意见或评论。
方法 #1: 创建一个 128 位整数类,将其整数内部存储为四个 32 位整数。这种方法的唯一问题是我不确定在处理两个操作数的各个块时如何处理上溢/下溢问题。
方法 #2: 使用 bcmath 扩展,因为这看起来像是它旨在解决的问题。采用这种方法我唯一担心的是 bcmath 扩展的比例设置,因为我的 128 位整数中不能有任何舍入错误;它们必须是精确的。我还担心最终能否将 bcmath 函数的结果转换为二进制字符串(稍后我需要将其放入一些 mcrypt 加密函数中)。
方法 #3: 将数字存储为二进制字符串(可能是 LSB 在前)。从理论上讲,我应该能够以这种方式存储任意大小的整数。我所要做的就是编写四个基本的算术函数来对两个二进制字符串执行加法/减法/乘法/除法并产生一个二进制字符串结果。这正是我需要移交给 mcrypt 的格式,所以这是一个额外的优点。这是我认为目前最有希望的方法,但我得到的一个症结是 PHP 没有为我提供任何操作单个位的方法(据我所知)。我相信我必须将它分解成字节大小的块(没有双关语),此时我关于处理方法 #1 中的上溢/下溢的问题适用。
【问题讨论】:
【参考方案1】:我实现了以下PEMDAS complaint BC evaluator,它可能对你有用。
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;
它会自动处理舍入错误,只需将精度设置为您需要的任何数字。
【讨论】:
【参考方案2】:PHP GMP extension 会更好。作为额外的奖励,您可以使用它进行十进制到二进制的转换,如下所示:
gmp_strval(gmp_init($n, 10), 2);
【讨论】:
谢谢,这看起来是一个非常漂亮的扩展! 如果您在 Windows 上运行 PHP x64,那么您对 GMP 扩展不走运。 GMP 库的维护者似乎没有将其移植到 Windows x64,尽管它可用于 Linux x64。要解决这个问题,如果您在项目中使用该数据库平台,您实际上可以使用 mysql。 MySQL 确实支持 64 位整数,并具有大量的布尔运算符和基本转换函数。我写了一个简单的类来在我的项目中促进这一点。【参考方案3】:据我所知,bcmath 扩展是您想要的。 PHP 手册中的数据有点稀疏,但是您可以通过使用 bcscale() 函数或大多数其他 bcmath 函数中的可选第三个参数来将精度设置为您所需要的。不太确定二进制字符串的事情,但是谷歌搜索告诉我你应该能够通过使用 pack() 函数来完成。
【讨论】:
【参考方案4】:已经有各种 classes available 用于此,因此您可能希望在编写自己的解决方案之前查看它们(如果确实仍然需要编写自己的解决方案)。
【讨论】:
以上是关于PHP中任意大整数的算术运算的主要内容,如果未能解决你的问题,请参考以下文章