对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了。其实,这也是一种精度越界之后产生的精度丢失问题。在我们的 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

===============

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

PHP中操作任意精度大小的GMP扩展学习的更多相关文章

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

    今天来学习的是关于数学方面的第一个扩展.对于数学操作来说,无非就是那些各种各样的数学运算,当然,整个程序软件的开发过程中,数学运算也是最基础最根本的东西之一.不管你是学得什么专业,到最后基本上都会要学 ...

  2. 自定义控件,上图下字的Button,图片任意指定大小

    最近处在安卓培训期,把自己的所学写成博客和大家分享一下,今天学的是这个自定义控件,上图下字的Button安卓自带,但是苦于无法设置图片大小(可以在代码修改),今天自己做了一个,首先看一下效果图,比较实 ...

  3. LINUX任意精度计算器BC用法

    [用途说明] Bash内置了对整数四则运算的支持,但是并不支持浮点运算,而bc命令可以很方便的进行浮点运算,当然整数运算也不再话下.手册页上说bc是An arbitrary precision cal ...

  4. c#中操作word文档-四、对象模型

    转自:http://blog.csdn.net/ruby97/article/details/7406806 Word对象模型  (.Net Perspective) 本文主要针对在Visual St ...

  5. 【译】在Asp.Net中操作PDF - iTextSharp - 利用列进行排版

    原文 [译]在Asp.Net中操作PDF - iTextSharp - 利用列进行排版 在使用iTextSharp通过ASP.Net生成PDF的系列文章中,前面的文章已经讲述了iTextSharp所涵 ...

  6. 【译】在Asp.Net中操作PDF - iTextSharp - 绘制矢量图

    原文 [译]在Asp.Net中操作PDF - iTextSharp - 绘制矢量图 在上一篇iTextSharp文章中讲述了如何将现有的图片插入PDF中并对其进行操作.但有时,你需要在PDF中绘制不依 ...

  7. 【译】在Asp.Net中操作PDF – iTextSharp - 操作图片

    原文 [译]在Asp.Net中操作PDF – iTextSharp - 操作图片 作为我的iTextSharp系列的文章的第七篇,开始探索使用iTextSharp在PDF中操作图片,理解本篇文章需要看 ...

  8. 【译】在Asp.Net中操作PDF – iTextSharp -利用块,短语,段落添加文本

    原文 [译]在Asp.Net中操作PDF – iTextSharp -利用块,短语,段落添加文本 本篇文章是讲述使用iTextSharp这个开源组件的系列文章的第三篇,iTextSharp可以通过As ...

  9. 【译】在Asp.Net中操作PDF - iTextSharp - 使用字体

    原文 [译]在Asp.Net中操作PDF - iTextSharp - 使用字体 紧接着前面我对iTextSharp简介博文,iTextSharp是一个免费的允许Asp.Net对PDF进行操作的第三方 ...

随机推荐

  1. 前端人员必会工具-apipost两分钟上手(2分钟玩转apipost)

    本文通过简单介绍如何利用ApiPost调试接口和快速的生成接口文档,让您初步体验ApiPost的魅力! 1. API写完想要测试?试试模拟发送一次请求 新建接口,我想模拟发送请求如下 curl --l ...

  2. 支持初始化数据的Zookeeper Docker镜像

    最近在做一个演示项目 https://github.com/cnscud/cavedemo, 自然为了方便, 也做了docker打包, 发现zookeeper的镜像没有导入初始化数据的功能, 于是自己 ...

  3. SQL 练习26

    查询 1990 年出生的学生名单 --方式1 SELECT * FROM Student WHERE Sage BETWEEN '1990-01-01' AND '1990-12-31' --方式2 ...

  4. reduce使用技巧

    一.使用reduce同时执行map(循环)和filter(过滤) 例如,将数组中的项的值加倍,然后只选择那些大于50的项 const numbers = [10, 20, 30, 40]; const ...

  5. [转]C# 互操作性入门系列(四):在C# 中调用COM组件

    传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...

  6. 使用VSCode创建第一个VUE项目

    vue init webpack vue_test回车,然后输入工程名称vue_test vue:Missing space before value for key 'components' 原因是 ...

  7. springCloud之路API路由网关Zuul

    1.简介 简单的理解就是,相当于在所有服务的调用前加了一层防火墙, 主要就是对外提供服务接口的时候,起到了请求的路由和过滤作用,也因此能够隐藏内部服务的接口细节,提高系统的安全性: 官方文档:http ...

  8. springboot如何使用事物注解方式

    1.在启动类Application中添加注解@EnableTransactionManagement import tk.mybatis.spring.annotation.MapperScan; i ...

  9. mfc HackerTools监控键盘按键

    string GetKey(int Key) { string KeyString = ""; //判断符号输入 const int KeyPressMask = 0x800000 ...

  10. 在Java泛型

    1,泛型的定义以及存在意义 泛型,即"参数化类型".就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传 ...