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之第二天
Oct 09 PHP
PHP 5.3.1 安装包 VC9 VC6不同版本的区别是什么
Jul 04 PHP
PHP在字符断点处截断文字的实现代码
Apr 21 PHP
PHP 调试工具Debug Tools
Apr 30 PHP
php的array数组和使用实例简明教程(容易理解)
Mar 20 PHP
PHP FTP操作类代码( 上传、拷贝、移动、删除文件/创建目录)
May 10 PHP
C#静态方法与非静态方法实例分析
Sep 22 PHP
php数组键值用法实例分析
Feb 27 PHP
php获取系统变量方法小结
May 29 PHP
yii2控制器Controller Ajax操作示例
Jul 23 PHP
php利用递归实现删除文件目录的方法
Sep 23 PHP
php自定义函数转换html标签示例
Sep 29 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 cookie的操作实现代码(登录)
2010/12/29 PHP
php对数组排序代码分享
2014/02/24 PHP
php实现比较两个字符串日期大小的方法
2015/05/12 PHP
微信 getAccessToken方法详解及实例
2016/11/23 PHP
点击广告后才能获得下载地址
2006/10/26 Javascript
javascript Ext JS 状态默认存储时间
2009/02/15 Javascript
深入理解JavaScript系列(10) JavaScript核心(晋级高手必读篇)
2012/01/15 Javascript
javascript闭包的理解
2015/04/01 Javascript
JS遍历数组及打印数组实例分析
2016/01/21 Javascript
JavaScript实现移动端滑动选择日期功能
2016/06/21 Javascript
Bootstrap时间选择器datetimepicker和daterangepicker使用实例解析
2016/09/17 Javascript
javascript实现获取指定精度的上传文件的大小简单实例
2016/10/25 Javascript
jQuery表单元素选择器代码实例
2017/02/06 Javascript
JavaScript表单即时验证 验证不成功不能提交
2017/08/31 Javascript
详解设置Webstorm 利用babel将ES6自动转码成ES5
2017/12/20 Javascript
jQuery实现获取选中复选框的值实例详解
2018/06/28 jQuery
解决vue-router在同一个路由下切换,取不到变化的路由参数问题
2018/09/01 Javascript
vue组件传值的实现方式小结【三种方式】
2020/02/05 Javascript
Python使用设计模式中的责任链模式与迭代器模式的示例
2016/03/02 Python
python实现判断一个字符串是否是合法IP地址的示例
2018/06/04 Python
python 地图经纬度转换、纠偏的实例代码
2018/08/06 Python
在Python运行时动态查看进程内部信息的方法
2019/02/22 Python
Python高阶函数、常用内置函数用法实例分析
2019/12/26 Python
最新2019Pycharm安装教程 亲测
2020/02/28 Python
实例讲解CSS3中的box-flex弹性盒属性布局
2016/06/09 HTML / CSS
Timberland俄罗斯官方网上商店:全球领先的户外品牌
2020/03/15 全球购物
什么是触发器(trigger)? 触发器有什么作用?
2013/09/18 面试题
高二化学教学反思
2014/01/30 职场文书
行政执法队伍作风整顿个人剖析材料
2014/10/11 职场文书
教师见习报告范文
2014/11/03 职场文书
学习焦裕禄观后感
2015/06/09 职场文书
堂吉诃德读书笔记
2015/06/30 职场文书
《水上飞机》教学反思
2016/02/20 职场文书
Mybatis是这样防止sql注入的
2021/12/06 Java/Android
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python
vue-cli3.0修改打包后的文件名和文件地址,打包后本地运行报错解决
2022/04/06 Vue.js