您现在的位置是:技术博客 > PHPPHP 磨人的精度问题 Lucas2020-12-01 21:01【代码】696人已围观 简介浮点数的计算结果为什么会不按常理“出牌”?深入理解,才能不再踩坑。 都知道关于浮点数的问题,处理不好,很容易程序“翻车”。其实几乎所有语言的浮点数计算都会出现问题,看似是bug,实际上并不是。 案例如下 ``` $a = '1754.40'; $b = 73.10 * 24; $c = 17.544 * 100; $d = 73.1 * 24; echo '$a = \'1754.40\'; // '; var_dump($a); echo '<br>'; echo '$b = 73.10 * 24; // '; var_dump($b); echo '<br>'; echo '$c = 17.544 * 100; // '; var_dump($c); echo '<br>'; echo '$d = 73.1 * 24; // '; var_dump($d); echo '<hr>比较结果如下:<hr>'; if($a == $b){ echo '$a 等于 $b'; }else{ echo '$a 不等于 $b'; } if($a == $c){ echo '<br>'; echo '$a 等于 $c'; }else{ echo '<br>'; echo '$a 不等于 $c'; } if($a == $d){ echo '<br>'; echo '$a 等于 $d'; }else{ echo '<br>'; echo '$a 不等于 $d'; } if($b == $c){ echo '<br>'; echo '$b 等于 $c'; }else{ echo '<br>'; echo '$b 不等于 $c'; } if($b == $d){ echo '<br>'; echo '$b 等于 $d'; }else{ echo '<br>'; echo '$b 不等于 $d'; } if($c == $d){ echo '<br>'; echo '$c 等于 $d'; }else{ echo '<br>'; echo '$c 不等于 $d'; } ``` 以上代码运行结果如下: ``` $a = '1754.40'; // string(7) "1754.40" $b = 73.10 * 24; // float(1754.4) $c = 17.544 * 100; // float(1754.4) $d = 73.1 * 24; // float(1754.4) 比较结果如下: $a 不等于 $b $a 等于 $c $a 不等于 $d $b 不等于 $c $b 等于 $d $c 不等于 $d ``` 那么在只比较值的情况下,为何 $c 不等于 $d 呢? 简化下上述代码,例子如下: ``` $a = 0.1; $b = 0.7; $c = $a + $b; $d = 0.8; echo '$a = 0.1; // '; var_dump($a); echo '<br>'; echo '$b = 0.7; // '; var_dump($b); echo '<br>'; echo '$c = $a + $b; // '; var_dump($c); echo '<br>'; echo '$d = 0.8; // '; var_dump($d); echo '<hr>比较结果如下:<hr>'; if($c == $d){ echo '$c 等于 $d'; }else{ echo '$c 不等于 $d'; } ``` 以上代码运行结果如下: ``` $a = 0.1; // float(0.1) $b = 0.7; // float(0.7) $c = $a + $b; // float(0.8) $d = 0.8; // float(0.8) 比较结果如下: $c 不等于 $d ``` 问题就很明显了,同样是 float(0.8) ,为何不等呢?别急,我们再变化下代码。 ``` $a = 0.9 - (0.3 + 0.6); // $a 打印的结果是 1.1102230246252E-16 echo '运行结果为:',$a; ``` 当我们在尝试多种浮点数加减的时候,就会发现并不是所有的情况都会出现偏差,那么究竟是为什么呢?我们又该如何处理精度问题。究其原因是二进制转换问题。这里有一篇<a href="https://blog.csdn.net/weixin_41042404/article/details/81276782" target='_blank'>关于小数如何转化成二进制的详细理解</a>的文章,需要的可以查看下。 PHP 通常使用 IEEE 754 双精度格式,关于浮点数的<a href="https://www.php.net/manual/zh/language.types.float.php" target='_blank'>PHP官方文档</a>。 顺便先抛出来文档中的一句话:永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数。 0.3的二进制为,只取64位 ``` 0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ``` 再将二进制转换成十进制:0.296875 ``` // 0 * pow(2, -1) + 1 * pow(2, -2) + 0 * pow(2, -3) + 0 * pow(2, -4) + 1 * pow(2, -5) + 1 * pow(2, -6) + 0 * pow(2, -7) + 0 * pow(2, -8); ``` 0.6的二进制为,只取64位 ``` 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 ``` 再将二进制转换成十进制:0.59765625 ``` // 1 * pow(2, -1) + 0 * pow(2, -2) + 0 * pow(2, -3) + 1 * pow(2, -4) + 1 * pow(2, -5) + 0 * pow(2, -6) + 0 * pow(2, -7) + 1 * pow(2, -8); ``` $a 和 $b 的十进制和为 0.89453125,结果当然就不等于 0.9了。 理论上讲在逻辑代码中处理无限小数时是没有很好的方法的,最好的方式是所有的浮点数尽可能转换成整形数然后再处理,或者每次运算都是用高精度的函数运算,然而不得不说的是,这一切也是建立在有限可知的小数位才能实现。 常用的高精度函数如下: bcadd — 加法 bccomp — 比较 bcdiv — 相除 bcmod — 求余数 bcmul — 乘法 bcpow — 次方 bcpowmod — 先次方然后求余数 bcscale — 给所有函数设置小数位精度 bcsqrt — 求平方根 bcsub — 减法 转载:感谢您对Lucas个人博客网站平台的认可,非常欢迎各位朋友分享到个人站长或者朋友圈,但转载请说明文章出处“来源Lucas个人博客”。 很赞哦! ( 0 ) 上一篇:时间日期相关问题的处理 下一篇:图像处理剪裁及拷贝 相关文章 高并发库存防控超量 Swoole 基础篇一(初识) Sublime代码格式化 Session与Cookie 点击排行 生活不止眼前的苟且,还有诗和远方 十年一觉电影梦 奥地利基茨比厄尔 禅修治愈身心 自律成就自我 零边际成本社会 Modern PHP 鸟哥的Linux私房菜 本栏推荐 要技术,更要有创意 定时任务 Curl无法发送https请求 Lnmp环境搭建 常用的SQL函数 Windows的cmd指令 ueditor工具栏浮动bug 有趣的js插件 标签云 git laravel swoole javascript vue ajax html css sql linux docker flask django nginx apache thinkphp markdown sublime wechat layui photoshop nodejs mysql windows composer java maven springboot mybatis IDE 猜你喜欢 Swoole 基础篇一(初识) PHP进程管理器 正则的快速上手 Sublime代码格式化 Lnmp环境搭建 高并发库存防控超量 Linux之top命令 常用的SQL函数 站点信息 建站时间:2018-05-01 在线人数:1人 文章统计:263篇 总浏览量:222667次 统计数据:百度统计 个人信息:扫描二维码查看