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

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+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)

相关推荐

  • PHP中命名空间是怎样的存在(一)?

    命名空间其实早在PHP5.3就已经出现了.不过大部分同学可能在各种框架的使用中才会接触到命名空间的内容,当然,现代化的开发也都离不开这些能够快速产出的框架.这次我们不从框架的角度,仅从简单的代码角度来 ...

  • 标梵学习PHP的压缩工具扩展包

    PHP的zip压缩工具扩展包学习 总算到了 PHP 的拿手好戏上场了,前面我们学习过 Bzip2 . LZF . Phar 和 rar 这些压缩相关扩展在 PHP 中的使用,不过它们要么是太冷门,要么 ...

  • 学习PHP中的任意精度扩展函数

    学习PHP中的任意精度扩展函数 今天来学习的是关于数学方面的第一个扩展.对于数学操作来说,无非就是那些各种各样的数学运算,当然,整个程序软件的开发过程中,数学运算也是最基础最根本的东西之一.不管你是学 ...

  • 如何在Win10中无损调整硬盘分区大小?

    为什么需要在Win10中无损调整硬盘分区大小? 最常见的情况,由于频繁的使用电脑,分区数据会逐渐增加,如果一个分区本身容量很小,空间不足的情况,尤其是对于系统C分区.空间不足将影响计算机的速度.那该如 ...

  • 震惊!重庆惊现一三八中可以任意读的房子,神不神奇?真相大跌眼镜

    这两天,突然朋友圈被这样一个神奇的楼盘刷屏了: 公寓还能读一三八初中,这么牛吗?是哪家的公寓啊? 原来是龙湖光年的公寓,不是卖了很久了吗,怎么突然使出这么厉害的大招了啊,是房子不好卖了吗?一三八指标任 ...

  • 心理学:如果一个人具备这五点中的任意两点,证明他很邪恶

    每天耕耘最有趣.最实用的心理学 人们在社会交往当中会常常去思考身边人的各种特点,有时会思考其性格,有时会去在乎他人的生活习惯,有时则会在意他人的其他特点,这就要求我们要"知己知彼,百战百胜. ...

  • 如何在Python中操作日期和时间

    编写Python程序,处理日期和时间经常会遇到,幸好Python本身集成了很多日期.时间相关的模块,让这件繁琐的工作变的方便.本文介绍Python中功能强大的datetime模块. datetime模 ...

  • Java JDBC 中获取 ResultSet 的大小

    当我们执行完一条Sql语句,获取到一个 ResultSet 对象后,有时我们需要立即知道到底返回了多少个元素,但是 ResultSet 并没有提供一个 size() 方法 or length 的属性, ...

  • 在PHP中操作临时文件

    在PHP中操作临时文件 关于文件相关的操作,想必大家已经非常了解了,在将来我们刷到手册中相关的文件操作函数时也会进行详细的讲解.今天,我们先来了解一下在 PHP 中关于临时文件相关的一些内容. 获取 ...

  • PHP中操作数据库的预处理语句

    PHP中操作数据库的预处理语句 今天这篇文章的内容其实也是非常基础的内容,不过在现代化的开发中,大家都使用框架,已经很少人会去自己封装或者经常写底层的数据库操作代码了.所以这回我们就来复习一下数据库中 ...

  • PHP中的MySQLi扩展学习(五)MySQLI_STMT对象操作

    PHP中的MySQLi扩展学习(五)MySQLI_STMT对象操作 就像 PDO 中的 PDO_Statment 对象一样,MySQLI_STMT 对象也是一个预处理语句所形成的对象,专门用来操作 M ...