Skip to content

GMP 大数运算

PHP 的 GMP 扩展(GNU Multiple Precision)提供了任意精度的大整数运算。与 BC Math 不同,GMP 专门针对整数运算优化,速度更快,支持位操作、模运算、素数检测等数论运算。在密码学、大数计算、组合数学等领域有广泛应用。

前置知识

阅读本节前,建议先了解:BC Math 任意精度Math 数学函数

基础概念

GMP vs BC Math

特性GMPBC Math
数据类型整数整数 + 小数
输入参数int/string/GMP字符串
速度更快较慢
位操作支持不支持
精度控制自动手动

安装

bash
# 编译安装
./configure --enable-gmp

# Ubuntu/Debian
sudo apt-get install php-gmp

基本运算

创建 GMP 数

php
<?php
declare(strict_types=1);

use GMP;

// 从整数创建
$num = gmp_init(12345678901234567890);

// 从字符串创建
$num = gmp_init('12345678901234567890');

// 从其他进制创建
$hex = gmp_init('FF', 16);     // 255
$oct = gmp_init('377', 8);      // 255
$bin = gmp_init('11111111', 2); // 255

// PHP 8.4+ 可以直接构造
// $num = new GMP('12345678901234567890');

// 转换为字符串
echo gmp_strval($num) . PHP_EOL;

// 转换为其他进制
echo gmp_strval(gmp_init(255), 16) . PHP_EOL; // ff
echo gmp_strval(gmp_init(255), 2) . PHP_EOL;  // 11111111

// 转换为整数
echo gmp_intval(gmp_init('42')) . PHP_EOL;   // 42

// 导出为二进制字符串
echo gmp_export(gmp_init('12345')) . PHP_EOL;

算术运算

php
<?php
declare(strict_types=1);

$a = gmp_init('123456789012345678901234567890');
$b = gmp_init('987654321098765432109876543210');

// 加法
echo gmp_strval(gmp_add($a, $b)) . PHP_EOL;

// 减法
echo gmp_strval(gmp_sub($a, $b)) . PHP_EOL;

// 乘法
echo gmp_strval(gmp_mul($a, $b)) . PHP_EOL;

// 除法(返回商和余数)
[$quotient, $remainder] = gmp_div_qr($a, $b);
echo "商: " . gmp_strval($quotient) . PHP_EOL;
echo "余: " . gmp_strval($remainder) . PHP_EOL;

// 模运算
echo gmp_strval(gmp_mod($a, gmp_init('100'))) . PHP_EOL;

// 幂运算
echo gmp_strval(gmp_pow(gmp_init(2), 100)) . PHP_EOL; // 2^100

// 模幂运算
echo gmp_strval(gmp_powm(gmp_init(2), 10, gmp_init(1000))) . PHP_EOL; // 24

// 平方根(向下取整)
echo gmp_strval(gmp_sqrt(gmp_init('144'))) . PHP_EOL; // 12

// 绝对值
echo gmp_strval(gmp_abs(gmp_init(-100))) . PHP_EOL; // 100

// 取反
echo gmp_strval(gmp_neg(gmp_init(100))) . PHP_EOL; // -100

// 符号
echo gmp_sign(gmp_init(-100)) . PHP_EOL; // -1
echo gmp_sign(gmp_init(0)) . PHP_EOL;    // 0
echo gmp_sign(gmp_init(100)) . PHP_EOL;  // 1

比较

php
<?php
declare(strict_types=1);

$a = gmp_init('100000000000000000000');
$b = gmp_init('200000000000000000000');

// gmp_cmp: 0 = 相等, 1 = a > b, -1 = a < b
echo gmp_cmp($a, $b) . PHP_EOL; // -1
echo gmp_cmp($b, $a) . PHP_EOL; // 1
echo gmp_cmp($a, $a) . PHP_EOL; // 0

// gmp_equal, gmp_gt, gmp_lt (PHP 8.4+)

位操作

php
<?php
declare(strict_types=1);

$num = gmp_init('0xFF'); // 255

// 与(AND)
echo gmp_strval(gmp_and($num, gmp_init('0x0F')), 16) . PHP_EOL; // f

// 或(OR)
echo gmp_strval(gmp_or($num, gmp_init('0x0F')), 16) . PHP_EOL; // ff

// 异或(XOR)
echo gmp_strval(gmp_xor($num, gmp_init('0x0F')), 16) . PHP_EOL; // f0

// 非(NOT)
echo gmp_strval(gmp_com($num), 16) . PHP_EOL;

// 左移
echo gmp_strval(gmp_mul_2exp(gmp_init(1), 10)) . PHP_EOL; // 1024 (1 << 10)

// 右移
echo gmp_strval(gmp_div_2exp(gmp_init(1024), 10)) . PHP_EOL; // 1 (1024 >> 10)

// 设置位
echo gmp_strval(gmp_setbit(gmp_init(0), 5)) . PHP_EOL; // 32

// 扫描位
echo gmp_scan0(gmp_init('0b1000'), 0) . PHP_EOL; // 3 (第一个0的位置)
echo gmp_scan1(gmp_init('0b0111'), 0) . PHP_EOL; // 1 (第一个1的位置)

// 位测试
echo gmp_testbit(gmp_init('0b100'), 2) ? '1' : '0' . PHP_EOL; // 1

// 统计1的位数
echo gmp_hamdist(gmp_init('0b10101'), gmp_init(0)) . PHP_EOL;
// 注意: gmp_popcount 统计1的数量 (PHP 7.0+)

数论函数

php
<?php
declare(strict_types=1);

// 最大公约数(GCD)
echo gmp_strval(gmp_gcd(gmp_init(48), gmp_init(18))) . PHP_EOL; // 6

// 最小公倍数(LCM)
echo gmp_strval(gmp_lcm(gmp_init(12), gmp_init(18))) . PHP_EOL; // 36

// 素数检测
echo gmp_prob_prime(gmp_init('99999999999999997'), 10) . PHP_EOL;
// 返回: 0 = 非素数, 1 = 可能素数, 2 = 确定素数

// 下一个素数
echo gmp_strval(gmp_nextprime(gmp_init(100))) . PHP_EOL; // 101

// 随机素数
$randomPrime = gmp_random_prime(256); // 256位素数
echo gmp_strval($randomPrime) . PHP_EOL;

// Legendre 符号
echo gmp_legendre(gmp_init(3), gmp_init(11)) . PHP_EOL;

// Jacobi 符号
echo gmp_jacobi(gmp_init(2), gmp_init(5)) . PHP_EOL;

// 阶乘
echo gmp_strval(gmp_fact(20)) . PHP_EOL; // 2432902008176640000

// 组合数 C(n, k)
echo gmp_strval(gmp_binomial(10, 3)) . PHP_EOL; // 120

// Fibonacci 数
echo gmp_strval(gmp_fib(10)) . PHP_EOL; // 55

实战示例

RSA 密钥生成辅助

php
<?php
declare(strict_types=1);

/**
 * 使用 GMP 辅助生成素数对
 */
function generatePrimePair(int $bitLength = 512): array
{
    $p = gmp_random_prime($bitLength);
    $q = gmp_random_prime($bitLength);

    // 确保 p != q
    while (gmp_cmp($p, $q) === 0) {
        $q = gmp_random_prime($bitLength);
    }

    // 计算 n = p * q
    $n = gmp_mul($p, $q);

    // 计算 phi(n) = (p-1) * (q-1)
    $phi = gmp_mul(gmp_sub($p, 1), gmp_sub($q, 1));

    return [
        'p'   => gmp_strval($p),
        'q'   => gmp_strval($q),
        'n'   => gmp_strval($n),
        'phi' => gmp_strval($phi),
    ];
}

// $keys = generatePrimePair(256);
// echo "n = " . $keys['n'] . PHP_EOL;
// echo "phi = " . $keys['phi'] . PHP_EOL;

注意事项

  • GMP 参数可以是整数、字符串或 GMP 对象
  • 大数运算比普通整数运算慢
  • PHP 8.4+ 引入了 GMP 类的改进

下一节

继续学习:Random 统一随机数生成器

参考链接