PHP从事金融行业,频繁的资金计算,稍有不慎,用户资金可能损失几十万,甚至更可怕...直接用例子吧:javascript0.1+为什么0.2不等于0.3?(正确结果:0.30000000000000004)为什么0.8*7不等于5.6?(正确结果:5.6000000000000005)PHPvar_dump(intval(0.58*100));由于浮点运算,正确的结果是57,不是58其实这些结果不是语言的bug,而是与语言的实现原理有关。js中所有的数字都统一为Numbers,包括整数,其实都是双精度(double)类型。而PHP会区分int和float。不管是什么语言,只要涉及到浮点运算,都会有类似的问题,使用时一定要注意。说明:如果使用php的+-*/来计算浮点数,可能会遇到一些错误的计算结果。比如上面的echointval(0.58*100);会打印57而不是58。这其实是计算机底层二进制不能准确表示浮点数的一个bug,是跨语言的。我用python也遇到过这个问题。所以基本上大部分语言都会提供类库或者函数库来进行精确计算。比如PHP有一个BC高精度函数库。后面会介绍一些常用的BC高精度函数。还是回到上面的问题57,58。为什么输出是57?这是PHP中的错误吗?要明白其中的道理,首先要知道浮点数的表示方式(IEEE754):浮点数,以64位长度(双精度)为例,会使用1位Signbit(E),11指数位(Q),52位尾数(M)表示(共64位)。符号位:最高位表示数据的正负,0表示正数,1表示负数。指数位:表示数据以2为底的幂,指数用偏移码表示尾数:表示数据小数点后的有效位数。这里的关键点是小数是用二进制表示的。关于二进制如何表示小数,大家可以自行百度,这里不再赘述。我们需要了解的关键是0.58是二进制表示的无限长值(下面的数字省略了隐含的1)。①二进制表示为0.58基本上是(52位):0010100111111111111100100010011011011111111111100100010011111111111.57二进制(52位)基本上是:0010001011110111101111111111011111111111111111111111111111111011110和52iftTwoCarcativeandifTwoCal0.569999999999。至于具体的0.58*100的浮点乘法,我们就不考虑那么详细了。有兴趣的可以看看(浮点数)。就用心算模糊的看一下吧……0.58*100=57.999999999 那么你对它求值,自然是57.... 可见这道题的重点是:“你看似有限的小数在计算机的二进制表示中是无限的” 因此,不要再认为这是一个PHP错误,这是这样的.....PHP浮点类型正在执行+-*%/不准确问题继续看一段代码:$a=0.1;$b=0.7;var_dump(($a+$b)==0.8);//false的打印值为布尔值false。为什么?PHP手册对于浮点数有以下警告信息:Warningandfloatingpointprecision。显然,像0.1或0.7这样的简单小数不能在不损失一点点精度的情况下丢失。大小写为内部二进制格式。这可能会导致令人困惑的结果:例如,floor((0.1+0.7)*10)通常会返回7而不是预期的8,因为结果的内部表示实际上类似于7.9999999999……。这与这样一个事实有关,即不可能用有限的位数精确地表达某些小数部分。例如,十进制的1/3变为0.3333333。..所以永远不要相信一个浮点数的结果精确到最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度的数学函数或gmp函数,那么我们应该将上面的公式改写为$a=0.1;$b=0.7;var_dump(bcadd($a,$b,2)==0.8);//true常用的高精度函数如下:bcadd--两个高精度数相加 bccomp--比较两个高精度数,返回-1,0,1 bcdiv--合并两个除以高-精度数 bcmod—求高精度数的余数 bcmul—两个高精度数相乘 bcpow—求高精度数字幂 bcpowmod—求高精度数字幂和模, bcscale在数论中非常常用——配置默认小数点位数,相当于"scale=" bcsqrt——求一个高精度数的平方根 bcsub——相减两个高精度数BC高精度函数库包括:加法、比较、除法、减法、余数、乘法、n次方、默认小数位数配置、平方。当涉及到涉及金钱的计算时,例如电子商务价格计算,这些函数很有用。/***两个高精度数字的比较**@accessglobal*@paramfloat$left*@paramfloat$right*@paramint$scale精确的小数位数**@returnint$left==$right返回0|$left<$right返回-1|$left>$rightreturns1*/var_dump(bccomp($left=4.45,$right=5.54,2));//-1/***两个高精度数相加**@accessglobal*@paramfloat$left*@paramfloat$right*@paramint$scale精确的小数位数**@returnstring*/var_dump(bcadd($left=1.0321456,$right=0.0243456,2));//1.05/***两个高精度数字相减**@accessglobal*@paramfloat$left*@paramfloat$right*@paramint$scale精确小数点数**@returnstring*/var_dump(bcsub($left=1.0321456,$right=3.0123456,2));//-1.98/***两个高精度数相除**@accessglobal*@paramfloat$left*@paramfloat$right*@paramint$scale的精确数小数位**@returnstring*/var_dump(bcdiv($left=6,$right=5,2));//1.20/***两个高精度数相乘**@accessglobal*@paramfloat$left*@paramfloat$right*@paramint$scale精确的小数点Number**@returnstring*/var_dump(bcmul($left=3.1415926,$right=2.4569874566,2));//7.71/***设置bc函数的小数点位数**@accessglobal*@paramint$scale精确的小数位数**@returnvoid*/bcscale(3);var_dump(bcdiv('105','6.55957'));//php7.116封装方式:/***精确加法*@param[type]$a[description]*@param[type]$b[description]*/functionmath_add($a,$b,$scale='2'){returnbcadd($a,$b,$scale);}/***精确减法*@param[type]$a[description]*@param[type]$b[description]*/functionmath_sub($a,$b,$scale='2'){returnbcsub($a,$b,$scale);}/***精确乘法*@param[type]$a[description]*@param[type]$b[description]*/functionmath_mul($a,$b,$scale='2'){returnbcmul($a,$b,$scale);}/***精确除法*@param[type]$a[description]*@param[type]$b[description]*/functionmath_p($a,$b,$scale='2'){returnbcp($a,$b,$scale);}/***精确余数/模数*@param[类型]$a[描述]*@param[类型]$b[描述]*/functionmath_mod($a,$b){returnbcmod($a,$b);}/***比较大小*@param[type]$a[description]*@param[type]$b[description]*大于return1等于return0小于return-1*/functionmath_comp($a,$b,$scale='5'){返回bccomp($a,$b,$scale);//与小数位比较}
