php精度计算的问题解析

时间:2021-05-26

PHP

var_dump(intval(0.58 * 100));

正确结果是 57,而不是 58

浮点运算惹的祸

其实这些结果都并非语言的 bug,但和语言的实现原理有关, js 所有数字统一为 Number, 包括整形实际上全都是双精度(double)类型。

而PHP会区分 int 还是 float。不管什么语言,只要涉及浮点运算,都是存在类似的问题,使用时一定要注意。

说明:如果用php的+-*/计算浮点数的时候,可能会遇到一些计算结果错误的问题,比如上面 的 echo intval( 0.58*100 );会打印57,而不是58,这个其实是计算机底层二进制无法精确表示浮点数的一个bug,是跨语言的,我用python也遇到这个问题。所以基本上大部 分语言都提供了精准计算的类库或函数库,比如php有BC高精确度函数库,稍后我绍一下一些常用的BC高精确度函数使用。

还是回到上面的57,58问题。

为啥输出是57啊? PHP的bug么?

要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):

  • 浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).
  • 符号位:最高位表示数据的正负,0表示正数,1表示负数。
  • 指数位:表示数据以2为底的幂,指数采用偏移码表示
  • 数:表示数据小数点后的有效数字.

这里的关键点就在于, 小数在二进制的表示, 关于小数如何用二进制表示, 大家可以百度一下, 我这里就不再赘述, 我们关键的要了解, 0.58 对于二进制表示来说, 是无限长的值(下面的数字省掉了隐含的1)..

0.58的二进制表示基本上(52位)是: 00101000111101011100001010001111010111000010100011110.57的二进制表示基本上(52位)是: 001000111101011100001010001111010111000010100011110而两者的二进制, 如果只是通过这52位计算的话,分别是:p($left=4.45, $right=5.54, 2));// -1/*** 两个高精度数相加* * @access global* @param float $left* @param float $right* @param int $scale 精确到的小数点位数* * @return string */var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));//1.05/*** 两个高精度数相减* * @access global* @param float $left* @param float $right* @param int $scale 精确到的小数点位数* * @return string */var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));//-1.98/*** 两个高精度数相除* * @access global* @param float $left* @param float $right* @param int $scale 精确到的小数点位数* * @return string */var_dump(bcdiv($left=6, $right=5, 2));//1.20/*** 两个高精度数相乘* * @access global* @param float $left* @param float $right* @param int $scale 精确到的小数点位数* * @return string */var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));//7.71/*** 设置bc函数的小数点位数* * @access global* @param int $scale 精确到的小数点位数* * @return void */ bcscale(3);var_dump(bcdiv('105', '6.55957')); //php7.1 16

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章