PHP浮点数精度问题汇总


Posted in PHP onMay 13, 2015

一、PHP浮点数精度损失问题

先看下面这段代码:

$f = 0.57;

echo intval($f * 100);  //56

结果可能有点出乎你的意外,PHP遵循IEEE 754双精度:

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

再来看看小数用二进制怎么表示:

乘2取整,顺序排列,即将小数部分乘以2,然后取整数部分,剩下的小数部分继续乘以2,然后取整数部分,剩下的小数部分又乘以2,一直取到小数部分,但是像0.57这样的小数像这样一直乘下去,小数部分不可能为0.有效位的小数用二进制表示却是无穷的。

0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101

如果只有52位的话,0.57 =》 0.56999999999999995

不难看出上面意外的结果了吧。

二、PHP浮点数的精度问题

先看问题:

$f = 0.58;

var_dump(intval($f * 100)); //为啥输出57

我相信有很多的同学有过这样的疑问。

具体原理可阅读“鸟哥”的一篇文章,那里有详细的解说:PHP浮点数的一个常见问题的解答

那么如何避免这种问题呢?
办法有很多,这里列举两个:
1. sprintf

substr(sprintf("%.10f", ($a/ $b)), 0, -7);

2. round (注意会进行四舍五入)
round($a/$b, 3);

或者你有更好的办法,也可以了留言告诉我。

三、PHP浮点数的一个常见问题的解答

关于PHP的浮点数, 我之前写过一篇文章: 关于PHP浮点数你应该知道的(All ‘bogus' about the float in PHP)

不过, 我当时遗漏了一点, 也就是对于如下的这个常见问题的回答:

<?php

    $f = 0.58;

    var_dump(intval($f * 100)); //为啥输出57

?>

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

我相信有很多的同学有过这样的疑问, 因为光问我类似问题的人就很多, 更不用说bugs.php.net上经常有人问…

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

浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).

符号位:最高位表示数据的正负,0表示正数,1表示负数。

指数位:表示数据以2为底的幂,指数采用偏移码表示

尾数:表示数据小数点后的有效数字.

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

0.58的二进制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111
0.57的二进制表示基本上(52位)是: 0010001111010111000010100011110101110000101000111101
而两者的二进制, 如果只是通过这52位计算的话,分别是:

0.58 -> 0.57999999999999996

0.57 -> 0.56999999999999995

至于0.58 * 100的具体浮点数乘法, 我们不考虑那么细, 有兴趣的可以看(Floating point), 我们就模糊的以心算来看… 0.58 * 100 = 57.999999999

那你intval一下, 自然就是57了….

可见, 这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的”

so, 不要再以为这是PHP的bug了, 这就是这样的…..

PHP 相关文章推荐
优化PHP代码的53条建议
Mar 27 PHP
php 函数中使用static的说明
Jun 01 PHP
浅析Apache中RewriteCond规则参数的详细介绍
Jun 30 PHP
5种PHP创建数组的实例代码分享
Jan 17 PHP
php中substr()函数参数说明及用法实例
Nov 15 PHP
ThinkPHP通过AJAX返回JSON的两种实现方法
Dec 18 PHP
php mysql like 实现多关键词搜索的方法
Oct 29 PHP
php动态读取数据清除最右边距的方法
Apr 12 PHP
Yii2中添加全局函数的方法分析
May 04 PHP
PHP设计模式之简单工厂和工厂模式实例分析
Mar 25 PHP
Yii框架中用response保存cookie,用request读取cookie的原理解析
Sep 04 PHP
php命令行模式代码实例详解
Feb 26 PHP
PHP生成器简单实例
May 13 #PHP
php实现比较两个字符串日期大小的方法
May 12 #PHP
php使用substr()和strpos()联合查找字符串中某一特定字符的方法
May 12 #PHP
PHP异常处理浅析
May 12 #PHP
php猴子选大王问题解决方法
May 12 #PHP
PHP嵌套输出缓冲代码实例
May 12 #PHP
php实现修改新闻时删除图片的方法
May 12 #PHP
You might like
php array_intersect()函数使用代码
2009/01/14 PHP
深入解析PHP中foreach语句控制数组循环的用法
2015/11/30 PHP
PhpStorm配置Xdebug调试的方法步骤
2019/02/02 PHP
PHP实现八皇后算法
2019/05/06 PHP
php扩展开发入门demo示例
2019/09/23 PHP
输入自动提示搜索提示功能的使用说明:sugggestion.txt
2013/09/02 Javascript
判断滚动条到底部的JS代码
2013/11/04 Javascript
jQuery 获取浏览器所在的IP地址的小例子
2013/11/08 Javascript
js实现仿百度风云榜可重复多次调用的TAB切换选项卡效果
2015/08/31 Javascript
JS实现自定义简单网页软键盘效果代码
2015/11/05 Javascript
javascript获取系统当前时间的方法
2015/11/19 Javascript
原生态js,鼠标按下后,经过了那些单元格的简单实例
2016/08/11 Javascript
BootStrap栅格系统、表单样式与按钮样式源码解析
2017/01/20 Javascript
微信小程序商城项目之淘宝分类入口(2)
2017/04/17 Javascript
解读vue生成的文件目录结构及说明
2017/11/27 Javascript
VUE2.0中Jsonp的使用方法
2018/05/22 Javascript
基于vue2的canvas时钟倒计时组件步骤解析
2018/11/05 Javascript
Angular6 Filter实现页面搜索的示例代码
2018/12/02 Javascript
在vue项目中优雅的使用SVG的方法实例详解
2018/12/03 Javascript
JS跨浏览器解析XML应用过程详解
2020/10/16 Javascript
python实现对一个完整url进行分割的方法
2015/04/29 Python
详解Python中的各种函数的使用
2015/05/24 Python
浅析python协程相关概念
2018/01/20 Python
解决Django数据库makemigrations有变化但是migrate时未变动问题
2018/05/30 Python
python高级特性和高阶函数及使用详解
2018/10/17 Python
在Pycharm中调试Django项目程序的操作方法
2019/07/17 Python
Python列表切片常用操作实例解析
2019/12/16 Python
解决pyinstaller打包运行程序时出现缺少plotly库问题
2020/06/02 Python
django的autoreload机制实现
2020/06/03 Python
如何基于python实现年会抽奖工具
2020/10/20 Python
Python操作word文档插入图片和表格的实例演示
2020/10/25 Python
Corelle官方网站:购买康宁餐具
2016/11/02 全球购物
日本热销NO.1胶原蛋白冻:Aishitoto爱希特多
2019/06/20 全球购物
《中彩那天》教学反思
2014/02/22 职场文书
2014年教学管理工作总结
2014/12/02 职场文书
2015年环保局工作总结
2015/05/22 职场文书