PHP中操作任意精度大小的GMP扩展学习

对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了 。其实,这也是一种精度越界之后产生的精度丢失问题 。在我们的 php 代码中,最大的整数非常大,我们可以通过 PHP_INT_MAX 来查看 。不过,当整数超过一定的位数之后,就会使用科学计数法来显示了,这个可不是我们想要的结果 。别着急,GMP 扩展就是专门用来应对这种情况的 。
GMP 扩展是随 PHP 源码包一起发布的,在安装扩展之前需要系统环境中先安装 gmp-devel ,在 centos 中直接 yun install gmp-devel 就可以了 。
超大数字的精度丢失问题我们先来看看直接打印输出超大的数字会发生什么 。
echo PHP_INT_MAX; // 92233720368547758071.2312312312312E+26$a = 123123123123123123123123123;echo $a, PHP_EOL; // 1.2312312312312E+26echo $a + 1, PHP_EOL; // 1.2312312312312E+26可以看到,显示的结果都是科学计数法的形式了 。而且对于简单的运算操作来说,也基本看不到有什么区别了 。就像我们最后给 $a + 1 的情况,它和原始的数据展示 出来的结果是一样的 。
$b = gmp_init("123123123123123123123123123");echo $b, PHP_EOL; // 123123123123123123123123123echo gmp_add($b, 1), PHP_EOL; // 123123123123123123123123124当我们使用 GMP 扩展后,就可以使用 gmp_init() 来实例化这样的超大数字 。打印的结果还是标准的数字格式 。不过,这里需要注意的是,这个扩展其实是将我们要操作的这种超大的数字转换成了字符串来表示 。
gmp_add() 是 GMP 的加法操作函数,非常简单,就是两个参数进行相加,然后返回的依然是一个 GMP 对象 。
var_dump($b);// object(GMP)#1 (1) {//["num"]=>//string(27) "123123123123123123123123123"//}echo $b + 1, PHP_EOL; // 123123123123123123123123124通过打印 gmp_init() 返回的 $b 对象就可以看出来 。它里面的内容其实是一个字符串了 。同时,这个对象还重写了 __toString() 方法,所以我们可以直接 echo 它 。另外,GMP 对象还重载了运算操作符,所以直接针对 GMP 对象进行日常的操作符运算也是没有问题的 。
简单运算操作除了重载的操作符之外,GMP 扩展也提供了一系列的运算操作函数,就像我们上面已经见过了 gmp_add() 一样 。
echo gmp_sub($b, 1), PHP_EOL; // 123123123123123123123123122echo gmp_mul($b, 2), PHP_EOL; // 246246246246246246246246246echo gmp_div("123123123123123123123123123", 3), PHP_EOL; // 41041041041041041041041041echo gmp_mod($b, 5), PHP_EOL; // 3这四个分别就是 减 、乘 、除 、余 的计算 。非常地简单,这里也就不多说了 。在这里需要注意的一点是,它们接收的参数可以是 int 类型,也可以是 字符串 类型 。就和 gmp_init() 接收的参数一样 。
【PHP中操作任意精度大小的GMP扩展学习】echo gmp_abs("-123123123123123123123123123"), PHP_EOL; // 123123123123123123123123123echo gmp_pow($b, 3), PHP_EOL; // 1866460784838622135378351047886265184644645186267890058355382138624840786461867echo gmp_sqrt($b), PHP_EOL; // 11096085937082这三个函数分别是取绝对值、乘方、二次方根的计算函数 。和普通的 Math 计算函数都是类似的 。
位操作GMP 扩展还可以方便地对数据进行位操作以及二进制操作 。比如位操作中的 与 、或 、异或 。
echo gmp_and($b, "2222222222"), PHP_EOL; // 2151965570echo gmp_or($b, "2222222222"), PHP_EOL; // 123123123123123123193379775echo gmp_xor($b, "3333333333"), PHP_EOL; // 123123123123123120012088038还可以将一个数字转换成二进制格式导出 。
echo gmp_export($b), PHP_EOL; // e?U??(c?O?当然,也有对应的从二进制导入的函数,这里我们就不做演示了 。大家可以自己在文档中查找相应的函数测试了解 。
$pop1 = gmp_init("10000101", 2); // 3echo gmp_popcount($pop1), PHP_EOL;$pop2 = gmp_init("11111110", 2); // 7echo gmp_popcount($pop2), PHP_EOL;gmp_popcount() 函数用于获取二进制表示的字符中的 1 的数量 。比如这段测试代码中返回的结果 。
$s1 = gmp_init("10111", 2);echo gmp_scan0($s1, 0), PHP_EOL; // 3$s2 = gmp_init("101110000", 2);echo gmp_scan0($s2, 0), PHP_EOL; // 0$s1 = gmp_init("10111", 2);echo gmp_scan1($s1, 0), PHP_EOL; // 0$s2 = gmp_init("101110000", 2);echo gmp_scan1($s2, 0), PHP_EOL; // 4gmp_scan0() 和 gmp_scan1() 函数则是分别查找第一个出现的 0 或 1 的位置 。它的第二个参数是指明从哪个位置开始查找 。另外,它们查找的方向都是从右向左开始查找,并且是从下标 0 的位置开始的哦 。


推荐阅读