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

10次阅读

共计 4070 个字符,预计需要花费 11 分钟才能阅读完成。

对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无奈显示或者操作了。其实,这也是一种精度越界之后产生的精度失落问题。在咱们的 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+26
echo $a + 1, PHP_EOL; // 1.2312312312312E+26

能够看到,显示的后果都是迷信计数法的模式了。而且对于简略的运算操作来说,也根本看不到有什么区别了。就像咱们最初给 $a + 1 的状况,它和原始的数据展现 进去的后果是一样的。

$b = gmp_init("123123123123123123123123123");
echo $b, PHP_EOL; // 123123123123123123123123123
echo 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; // 123123123123123123123123122
echo gmp_mul($b, 2), PHP_EOL; // 246246246246246246246246246
echo gmp_div("123123123123123123123123123", 3), PHP_EOL; // 41041041041041041041041041
echo gmp_mod($b, 5), PHP_EOL; // 3

这四个别离就是 减、乘、除、余 的计算。十分地简略,这里也就不多说了。在这里须要留神的一点是,它们接管的参数能够是 int 类型,也能够是 字符串 类型。就和 gmp_init() 接管的参数一样。

echo gmp_abs("-123123123123123123123123123"), PHP_EOL; // 123123123123123123123123123
echo gmp_pow($b, 3), PHP_EOL; // 1866460784838622135378351047886265184644645186267890058355382138624840786461867
echo gmp_sqrt($b), PHP_EOL; // 11096085937082

这三个函数别离是取绝对值、乘方、二次方根的计算函数。和一般的 Math 计算函数都是相似的。

位操作

GMP 扩大还能够不便地对数据进行位操作以及二进制操作。比方位操作中的 与、或、异或。

echo gmp_and($b, "2222222222"), PHP_EOL; // 2151965570
echo gmp_or($b, "2222222222"), PHP_EOL; // 123123123123123123193379775
echo gmp_xor($b, "3333333333"), PHP_EOL; // 123123123123123120012088038

还能够将一个数字转换成二进制格局导出。

echo gmp_export($b), PHP_EOL; // e�U��(c�O�

当然,也有对应的从二进制导入的函数,这里咱们就不做演示了。大家能够本人在文档中查找相应的函数测试理解。

$pop1 = gmp_init("10000101", 2); // 3
echo gmp_popcount($pop1), PHP_EOL;
$pop2 = gmp_init("11111110", 2); // 7
echo 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; // 4

gmp_scan0() 和 gmp_scan1() 函数则是别离查找第一个呈现的 0 或 1 的地位。它的第二个参数是指明从哪个地位开始查找。另外,它们查找的方向都是从右向左开始查找,并且是从下标 0 的地位开始的哦。

其它运算操作

生成随机数

echo gmp_random_range("10000000000000", "99999999999999999"), PHP_EOL; // 83490559526159213
// 12500000000
echo gmp_random_bits(99999),PHP_EOL; // 289814632948807684404778811091812938699609………………

就和一般的生成随机数的函数一样,只不过 GMP 扩大库上面的这两个函数可能生成的数字范畴更大,而且返回的也是 GMP 对象的格局。对于 gmp_random_bits() 来说,最大的范畴是 12500000000,我的机子如果应用这个随机因子的话间接就会报超出内存了。而应用 99999 这个随机因子生成的随机数字也曾经十分大了,大家能够本人尝试一下。

阶乘

这个是一般的 Math 库中所没有的函数。间接帮咱们计算阶乘的后果,不必本人写算法了哦。

echo gmp_fact(5), PHP_EOL; // 120    5*4*3*2*1=120
echo gmp_fact(50), PHP_EOL; // 30414093201713378043612608166064768844377641568960512000000000000 50*49*48…………*2*1

素数

除了阶乘之外,GMP 还提供了十分高大上的间接获取和判断素数的函数。一般来说,素数(质数)也是面试中十分常见的算法题目,咱们在面试的时候还是要把握本人手写的能力,然而手写完之后能和面试官说一下 GMP 中曾经有现成的函数了置信也会带来一些加分。

echo gmp_nextprime(10), PHP_EOL; // 11
echo gmp_nextprime(1000), PHP_EOL; // 1009
echo gmp_prob_prime(6), PHP_EOL; // 0
echo gmp_prob_prime("1111111111111111111"), PHP_EOL; // 1
echo gmp_prob_prime(7), PHP_EOL; // 2

gmp_nextprime() 是获取指定数字之后的下一个素数是多少。gmp_prob_prime() 则是判断给写的数字是否是素数,它有三种后果,0 示意不是素数,1 示意可能(疑似)素数,2 示意确定是素数。

数据的符号信息

echo gmp_sign("500"), PHP_EOL; // 1
echo gmp_sign("-500"), PHP_EOL; // -1
echo gmp_sign("0"), PHP_EOL; // 0

最初这个 gmp_sign() 函数用来示意给定数据的符号信息,也就是正负数。它也是三种后果,1 示意负数,-1 示意正数,0 示意 0。为什么会有一个非凡的 0 存在呢?因为 0 即不是负数也不是正数呀,它自身就是一个非凡的存在。

总结

对于 GMP 扩大还有很多办法并没有一一列举进去,在这里只是筛选了一些比拟罕用的内容给大家介绍一下。虽说是刷文档,但也不能间接照搬文档过去,所以更多的内容大家还是自行去文档中查阅,咱们学习的目标次要就是晓得有这么个货色,不至于在实在的业务需要中踫到了相干的内容时抓瞎。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/8.PHP 中操作任意精度大小的 GMP 扩大学习.php

参考文档:

https://www.php.net/manual/zh/book.gmp.php

===========

各自媒体平台均可搜寻【硬核项目经理】

正文完
 0