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 前一天或后一天的日期
Jun 28 PHP
php 从数据库提取二进制图片的处理代码
Sep 09 PHP
在IIS7.0下面配置PHP 5.3.2运行环境的方法
Apr 13 PHP
PHP 第二节 数据类型之转换
Apr 28 PHP
深入PHP操作MongoDB的技术总结
Jun 02 PHP
PHP实现根据浏览器跳转不同语言页面代码
Aug 02 PHP
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)
Aug 18 PHP
php单态设计模式(单例模式)实例
Nov 18 PHP
浅谈PHP中output_buffering
Jul 13 PHP
PHP生成各种随机验证码的方法总结【附demo源码】
Jun 05 PHP
PHP异常类及异常处理操作实例详解
Dec 19 PHP
PHP 进程池与轮询调度算法实现多任务的示例代码
Nov 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获取文件行数的方法
2015/06/10 PHP
YII Framework框架教程之安全方案详解
2016/03/14 PHP
Iframe thickbox2.0使用的方法
2009/03/05 Javascript
select 控制网页内容隐藏于显示的实现代码
2010/05/25 Javascript
jquery里的each使用方法详解
2010/12/22 Javascript
jQuery弹性滑动导航菜单实现思路及代码
2013/05/02 Javascript
一个JavaScript的求爱小特效
2014/05/09 Javascript
JavaScript中Number.NEGATIVE_INFINITY值的使用详解
2015/06/05 Javascript
javascript判断复选框是否选中的方法
2015/10/16 Javascript
谈谈javascript中使用连等赋值操作带来的问题
2015/11/26 Javascript
js实现继承的5种方式
2015/12/01 Javascript
关于Google发布的JavaScript代码规范你要知道哪些
2018/04/04 Javascript
每天学点Vue源码之vm.$mount挂载函数
2019/03/11 Javascript
vue实现新闻展示页的步骤详解
2019/04/11 Javascript
基于js实现逐步显示文字输出代码实例
2020/04/02 Javascript
js实现3D粒子酷炫动态旋转特效
2020/09/13 Javascript
javascript局部自定义鼠标右键菜单
2020/12/08 Javascript
Python变量和字符串详解
2017/04/29 Python
python基础教程项目四之新闻聚合
2018/04/02 Python
pandas多级分组实现排序的方法
2018/04/20 Python
DRF跨域后端解决之django-cors-headers的使用
2019/01/27 Python
Python使用sqlalchemy模块连接数据库操作示例
2019/03/13 Python
pytorch模型存储的2种实现方法
2020/02/14 Python
如何将tensorflow训练好的模型移植到Android (MNIST手写数字识别)
2020/04/22 Python
python交互模式基础知识点学习
2020/06/18 Python
解决python和pycharm安装gmpy2 出现ERROR的问题
2020/08/28 Python
Django数据统计功能count()的使用
2020/11/30 Python
HTML5使用DOM进行自定义控制示例代码
2013/06/08 HTML / CSS
Java平台和其他软件平台有什么不同
2015/06/05 面试题
师范院校学生自荐信范文
2013/12/27 职场文书
小区推广策划方案
2014/06/06 职场文书
出差报告范文
2014/11/06 职场文书
教师个人师德工作总结2015
2015/05/12 职场文书
工作后的感想
2015/08/07 职场文书
热爱劳动主题班会
2015/08/14 职场文书
《秋天的雨》教学反思
2016/02/19 职场文书