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 array 的加法操作代码
Jul 24 PHP
php学习之流程控制实现代码
Jun 09 PHP
关于PHP内存溢出问题的解决方法
Jun 25 PHP
PHP根据IP地址获取所在城市具体实现
Nov 27 PHP
php实现的百度搜索某地天气的小偷代码
Apr 23 PHP
php中return的用法实例分析
Feb 28 PHP
PHP实现的memcache环形队列类实例
Jul 28 PHP
ZendFramework2连接数据库操作实例
Apr 18 PHP
PHP7 echo和print语句实例用法
Feb 15 PHP
Yii 框架控制器创建使用及控制器响应操作示例
Oct 14 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
基于HTTP长连接的&quot;服务器推&quot;技术的php 简易聊天室
2009/10/31 PHP
PHP转换IP地址到真实地址的方法详解
2013/06/09 PHP
PHP函数实现分页含文本分页和数字分页
2014/10/23 PHP
微信 开发生成带参数的二维码的实例
2016/11/23 PHP
PHP编程实现脚本异步执行的方法
2017/08/09 PHP
Django 中 cookie的使用
2017/08/17 PHP
Add Formatted Data to a Spreadsheet
2007/06/12 Javascript
Mootools 1.2教程 函数
2009/09/15 Javascript
一个简单的JavaScript数据缓存系统实现代码
2010/10/24 Javascript
JavaScript初学者建议:不要去管浏览器兼容
2014/02/04 Javascript
node.js插件nodeclipse安装图文教程
2020/10/19 Javascript
javascript检测移动设备横竖屏
2016/05/21 Javascript
浅析JavaScript的几种Math函数,random(),ceil(),round(),floor()
2016/12/22 Javascript
vue-cli+webpack记事本项目创建
2017/04/01 Javascript
Vue input控件通过value绑定动态属性及修饰符的方法
2017/05/03 Javascript
详解mpvue scroll-view自动回弹bug解决方案
2018/10/01 Javascript
JQuery事件委托原理与用法实例分析
2019/05/13 jQuery
深入理解 JS 垃圾回收
2019/06/03 Javascript
JSX在render函数中的应用详解
2019/09/04 Javascript
《javascript设计模式》学习笔记五:Javascript面向对象程序设计工厂模式实例分析
2020/04/08 Javascript
[02:01]BBC DOTA2国际邀请赛每日综述:八强胜者组鏖战,中国队喜忧参半
2014/07/19 DOTA
[48:24]完美世界DOTA2联赛循环赛LBZS vs Forest 第一场 10月30日
2020/10/31 DOTA
Python中List.index()方法的使用教程
2015/05/20 Python
python实现贪吃蛇小游戏
2020/03/21 Python
图文详解Django使用Pycharm连接MySQL数据库
2019/08/09 Python
Django 博客实现简单的全文搜索的示例代码
2020/02/17 Python
Python while true实现爬虫定时任务
2020/06/08 Python
keras的backend 设置 tensorflow,theano操作
2020/06/30 Python
Python基于Serializer实现字段验证及序列化
2020/11/04 Python
Python图像读写方法对比
2020/11/16 Python
HTML5的文档结构和新增标签完全解析
2017/04/21 HTML / CSS
绘儿乐产品官方在线商店:Crayola.com
2019/09/07 全球购物
疾病捐款倡议书
2014/05/13 职场文书
信访工作汇报材料
2014/10/27 职场文书
2015年环境整治工作总结
2015/05/22 职场文书
军训新闻稿范文
2015/07/17 职场文书