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 相关文章推荐
phpMyAdmin 安装教程全攻略
Mar 19 PHP
坏狼的PHP学习教程之第2天
Jun 15 PHP
PHP 采集心得技巧
May 15 PHP
php获取目录所有文件并将结果保存到数组(实例)
Oct 25 PHP
Zend Framework 2.0事件管理器(The EventManager)入门教程
Aug 11 PHP
11个PHPer必须要了解的编程规范
Sep 22 PHP
php+ajax实时刷新简单实例
Feb 25 PHP
php生成不重复随机数、数组的4种方法分享
Mar 30 PHP
CI框架集成Smarty的方法分析
May 17 PHP
php使用pdo连接sqlite3的配置示例
May 27 PHP
PHP实现的多维数组排序算法分析
Feb 10 PHP
tp5框架的增删改查操作示例
Oct 31 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 内存缓存加速功能memcached安装与用法
2009/09/03 PHP
PHP 遍历文件实现代码
2011/05/04 PHP
php array_intersect比array_diff快(附详细的使用说明)
2011/07/03 PHP
如何使用jQuery+PHP+MySQL来实现一个在线测试项目
2015/04/26 PHP
PHP+AJAX实现投票功能的方法
2015/09/28 PHP
学习YUI.Ext第七日-View&amp;JSONView Part Two-一个画室网站的案例
2007/03/10 Javascript
ListBox实现上移,下移,左移,右移的简单实例
2014/02/13 Javascript
JavaScript表单验证实例之验证表单项是否为空
2016/01/10 Javascript
JS前端笔试题分析
2016/12/19 Javascript
JS实现商品筛选功能
2020/08/19 Javascript
使用JavaScript实现链表的数据结构的代码
2017/08/02 Javascript
node.js实现的装饰者模式示例
2017/09/06 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
2018/11/29 Javascript
[00:52]玛尔斯技能全介绍
2019/03/06 DOTA
Python通过OpenCV的findContours获取轮廓并切割实例
2018/01/05 Python
python+opencv实现动态物体追踪
2018/01/09 Python
python如何为创建大量实例节省内存
2018/03/20 Python
利用python将json数据转换为csv格式的方法
2018/03/22 Python
python微元法计算函数曲线长度的方法
2018/11/08 Python
Python图像处理之直线和曲线的拟合与绘制【curve_fit()应用】
2018/12/26 Python
对python周期性定时器的示例详解
2019/02/19 Python
python BlockingScheduler定时任务及其他方式的实现
2019/09/19 Python
PYTHON实现SIGN签名的过程解析
2019/10/28 Python
python实现将视频按帧读取到自定义目录
2019/12/10 Python
Pyspark读取parquet数据过程解析
2020/03/27 Python
python实现图像外边界跟踪操作
2020/07/13 Python
创业计划书中包含的9个方面
2013/12/26 职场文书
经济管理专业毕业生自荐信范文
2014/01/02 职场文书
社会实践心得体会
2014/01/03 职场文书
《囚绿记》教学反思
2014/03/01 职场文书
明信片寄语大全
2014/04/08 职场文书
2014办公室年度工作总结
2014/12/09 职场文书
平安建设汇报材料
2014/12/29 职场文书
基层党建工作简报
2015/07/21 职场文书
高一化学教学反思
2016/02/22 职场文书
Nebula Graph解决风控业务实践
2022/03/31 MySQL