今天的第一个数学扩展。对于数学运算,无非就是各种数学运算。当然,数学运算也是整个程序软件开发过程中最基本、最根本的东西之一。不管你学什么专业,最后基本上都会学数据结构和算法,而算法其实就是在研究如何用数学来优化各种排序和搜索能力。PHP在底层为我们准备了很多数学计算函数,让我们一一学习吧。什么是精准的问题关于精准的问题,很多做过金融工作的小伙伴可能都不陌生。尤其是前端同学,如果在js中实现1.1+2.2,得到的结果往往不是如你所愿。这是关于浮点数的存储。我们都知道,在编程世界中,任何数据其实都是以二进制的形式存在于最底层的。但是浮点数由于有小数点的存在,存储起来比较复杂,所以经常会出现这种丢精度的问题。但是很多人会奇怪,在PHP中直接执行1.1+2.2的结果竟然是正确的。好像不存在这种精度损失的问题。呵呵,只能说你太年轻了,不简单。精度损失的问题不是哪个语言的问题,基本上所有的语言都有这样的问题,只是表现形式不同而已。bc精度计算我们先来看看PHP环境下精度的损失是怎么显示的。$a=0.58;echo$a*100,PHP_EOL;//58echointval($a*100),PHP_EOL;//57echo(int)($a*100),PHP_EOL;//57echointval(bcmul($a,100)),PHP_EOL;//58我们定义了一个变量$a,它的内容是0.58。这时候我们直接把他乘以100,结果好像没什么问题。但是如果我们强制为int类型,就会出现问题。明明是58,怎么就变成了57呢?其实经过浮点运算,结果不是58,而是57.99999999999999这样的数。如果我们直接echo,会按string转换,直接输出58,但如果按int转换,不管是inval()还是(int),都会按照舍弃小数的规则进行转换当int被强制时。因此,结果变为57。直接回显往往让我们觉得PHP中好像没有丢精度的问题,但实际上这个问题是真实存在的。很多时候,比如存入数据库,或者转成json格式,都会发现问题。如果想要计算准确,可以使用bc扩展相关函数,也就是我们上次演示的bcmul()函数。它的作用是将第一个参数乘以第二个参数,得到的结果也是高精度的,即精度准确的结果。下面我们通过json格式的转换来看看加减乘除的准确率。echojson_encode(['a1'=>$a,//"a1":0.58'a2'=>$a*100,//"a2":57.99999999999999'a3'=>intval($a*100),//"a3":57'a4'=>floatval($a*100),//"a4":57.99999999999999'a5'=>floatval($a),//"a5":0.58'a6'=>intval(bcmul($a,100)),//"a6":58'a7'=>1.1+2.2,//"a7":3.3000000000000003'a8'=>floatval(bcadd(1.1,2.2,10)),//"a8":3.3'a9'=>2-1.1,//"a9":0.8999999999999999'a10'=>floatval(bcsub(2,1.1,10)),//"a10":0.9'a11'=>floatval($a*100/10),//"a11":5.799999999999999'a12'=>floatval(bcdiv($a*100,10,10)),//"a12":5.8'a13'=>10%2.1,//"a13":0'a14'=>bcmod(10,2.1),//"a14":"1"'a15'=>pow(1.1,2),//"a15":1.2100000000000002'a16'=>bcpow(1.1,2,30),//"a16":"1.2100000000000000000000000000"'a17'=>sqrt(1.1),//"a17":1.0488088481701516=1b.18qrt(18qrt)).c18'),30),//"a18":"1.048808848170151546991453513679"]),PHP_EOL;通过这段代码应该可以清楚的看出PHP中是否存在精度丢失问题,json_encode()在转换数据的时候会根据字段的类型进行转换,所以精度问题会比较多很明显,这也是很多同学在后端计算的时候明明没有问题,但是通过json输出到前端的时候会发现数据有精度问题的原因。a1~a6就是我们第一次测试的内容代码,可以清楚的看到一般用$a*100的结果真的是57.99999999999999,a7和a8是加法的示范,怎么样,PHP中1.1+2.2的结果其实和JS中是一样的。通过bcadd()可以处理加法的精度,同理a9和a10是减法问题,通过bcsub()可以得到高精度的减法计算结果,bcdiv()是用来处理除法的,注意这些函数有一个ird参数,表示保留的小数位数。我们保留了10位小数。目的是希望如果出现精度损失的问题,可以和原来的计算进行对比。bcmod()的余数计算对应的就是%计算符号的作用。一般情况下,10%2的结果为0是正常的,但是这里我们计算10%2.1结果也是0,而使用bcmod()后结果为1,才是正确的结果。bcpow()是计算幂,对应普通函数中的pow()函数。同样在这里,我们在普通函数中计算2的1.1次方时存在精度问题。使用bcpow()我们显示了30位小数,并没有发现精度异常。这里需要注意的是,如果bcpow()指定了小数位数,就会显示出来,即使计算结果没有小数点,也会显示为0。上面的其他函数不会这样,会只有当确实有小数时才会显示。最后一个是bcsqrt()函数,它是第二个的平方根。这个没有溢出的号码可以给我们测试一下,如果你用过发现溢出了可以留言。比较函数完成了各种精度计算的功能。接下来我们来看一下数字比较的问题。echobccomp(1,2),PHP_EOL;//-1echobccomp(1.00001,1,3),PHP_EOL;//0echobccomp(1.00001,1,5),PHP_EOL;//1bccomp()函数是根据小数点进行数字精度比较的函数。如果参数1小于参数2,其返回结果为-1,如果大于则为1,如果等于则为0。第三个参数用户决定比较哪个位。在此示例中,我们可以看到1.00001和1仅与小数点后第三位进行比较时相等。而如果比较小数点后五位,就会体现出它们的差异。设置小数点和bcpowmod函数最后我们再看两个函数。BCSCALE(30);echoBCMOD(BCPOW(5,2),2),PHP_EOL;//1.00000000000000000000000000000000ECHOBCPowmod(5,2,2),php_eol;//1.000000000000000000000000000000000000000000bcscale()是在global.设置该函数后,如果不写小数点后三位函数,上面介绍的所有函数都会以bcscale()的设置为准。bcpowmod()函数的作用和第二行的测试代码一样,都是执行一次bcpow(),然后再执行一次bcmod()。它的使用场景不多,但是写起来很方便。总结一下今天的内容,除了bc相关的计算函数外,还谈到了精度的问题,这是各种语言都存在的问题。其实说实话,在我们日常开发中,以分为单位的金额等带小数点的数据存储是最好的。也就是说在后台保存和计算的数据都是整型数据。前端显示时,除以100即可,保留两位小数。这样可以极大的保证数据的准确性不会丢失。另外,关于PHP中的精度问题,可以参考下面第二个链接鸟哥博客上的描述。我们的例子0.58*100也是从他的博客里拿来的例子。测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/7。学习PHP中的任意精度扩展函数。PHP参考文档:https://www.php。net/manual/zh/book.bc.phphttps://www.laruence.com/2013/03/26/2884.html各媒体平台均可搜索【硬核项目经理】
