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 进度条实现代码
Mar 10 PHP
关于mysql字符集设置了character_set_client=binary 在gbk情况下会出现表描述是乱码的情况
Jan 06 PHP
深入解读php中关于抽象(abstract)类和抽象方法的问题分析
Jan 03 PHP
smarty实现多级分类的方法
Dec 05 PHP
PHP原生函数一定好吗?
Dec 08 PHP
php正则preg_replace_callback函数用法实例
Jun 01 PHP
PHP实现删除字符串中任何字符的函数
Aug 11 PHP
非常经典的PHP文件上传类分享
May 15 PHP
php使用pdo连接sqlite3的配置示例
May 27 PHP
php实现常见图片格式的水印和缩略图制作(面向对象)
Jun 15 PHP
PHP基于正则批量替换Img中src内容实现获取缩略图的功能示例
Jun 07 PHP
PHPUnit测试私有属性和方法功能示例
Jun 12 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 读取shell管道传输过来的内容
2010/03/01 PHP
让php处理图片变得简单 基于gb库的图片处理类附实例代码下载
2011/05/17 PHP
解析csv数据导入mysql的方法
2013/07/01 PHP
phpmailer中文乱码问题的解决方法
2014/04/22 PHP
php ajax实现文件上传进度条
2016/03/29 PHP
php表单加入Token防止重复提交的方法分析
2016/10/10 PHP
PHPExcel实现的读取多工作表操作示例
2020/04/14 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
javascript 类方法定义还是有点区别
2009/04/15 Javascript
JQuery toggle使用分析
2009/11/16 Javascript
jQuery学习笔记之Helloworld
2010/12/22 Javascript
用Javascript评估用户输入密码的强度(Knockout版)
2011/11/30 Javascript
jquery中$.post()方法的简单实例
2014/02/04 Javascript
浅谈javascript中createElement事件
2014/12/05 Javascript
JavaScript开发人员的10个关键习惯小结
2014/12/05 Javascript
Python脚本后台运行的几种方式
2015/03/09 Javascript
微信小程序顶部可滚动导航效果
2017/10/31 Javascript
将jquery.qqFace.js表情转换成微信的字符码
2017/12/01 jQuery
jQuery实现的手动拖动控制进度条效果示例【测试可用】
2018/04/18 jQuery
Spring boot 和Vue开发中CORS跨域问题解决
2018/09/05 Javascript
JS编写兼容IE6,7,8浏览器无缝自动轮播
2018/10/12 Javascript
vue监听dom大小改变案例
2020/07/29 Javascript
Python基于生成器迭代实现的八皇后问题示例
2018/05/23 Python
Python3中的列表生成式、生成器与迭代器实例详解
2018/06/11 Python
python实现比对美团接口返回数据和本地mongo数据是否一致示例
2019/08/09 Python
django 自定义filter 判断if var in list的例子
2019/08/20 Python
python用pip install时安装失败的一系列问题及解决方法
2020/02/24 Python
pytorch加载语音类自定义数据集的方法教程
2020/11/10 Python
利用python进行文件操作
2020/12/04 Python
Python爬取你好李焕英豆瓣短评生成词云的示例代码
2021/02/24 Python
HTML5之web workers_动力节点Java学院整理
2017/07/17 HTML / CSS
希尔顿酒店中国网站:Hilton中国
2017/03/11 全球购物
美国按摩椅批发网站:Titan Chair
2018/12/27 全球购物
银行实习自我鉴定
2013/10/12 职场文书
租房协议书样本
2014/08/20 职场文书
python unittest单元测试的步骤分析
2021/08/02 Python