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语法速查表
Jan 02 PHP
删除数组元素实用的PHP数组函数
Aug 18 PHP
php中的观察者模式
Mar 24 PHP
php中强制下载文件的代码(解决了IE下中文文件名乱码问题)
May 09 PHP
PHP数字字符串左侧补0、字符串填充和自动补齐的几种方法
May 10 PHP
php自定义hash函数实例
May 05 PHP
php支持中文字符串分割的函数
May 28 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
Nov 09 PHP
PHP中PDO的事务处理分析
Apr 07 PHP
PHP利用Socket获取网站的SSL证书与公钥
Jun 18 PHP
PHP join()函数用法与实例讲解
Mar 11 PHP
Yii2框架自定义类统一处理url操作示例
May 25 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
咖啡豆分级制度 咖啡豆等级分类 咖啡豆是按口感分类的吗?
2021/03/05 新手入门
PHP Global变量定义当前页面的全局变量实现探讨
2013/06/05 PHP
解析PHP中常见的mongodb查询操作
2013/06/20 PHP
php采用curl实现伪造IP来源的方法
2014/11/21 PHP
动态的创建一个元素createElement及删除一个元素
2014/01/24 Javascript
深入探秘jquery瀑布流的实现
2016/01/30 Javascript
jQuery简单获取DIV和A标签元素位置的方法
2017/02/07 Javascript
老生常谈angularjs中的$state.go
2017/04/24 Javascript
nodejs入门教程一:概念与用法简介
2017/04/24 NodeJs
js使用html2canvas实现屏幕截取的示例代码
2017/08/28 Javascript
vue中element组件样式修改无效的解决方法
2018/02/03 Javascript
详解webpack 入门与解析
2018/04/09 Javascript
如何用原生js写一个弹窗消息提醒插件
2019/05/24 Javascript
electron-vue开发环境内存泄漏问题汇总
2019/10/10 Javascript
vscode 配置vue+vetur+eslint+prettier自动格式化功能
2020/03/23 Javascript
如何通过Proxy实现JSBridge模块化封装
2020/10/22 Javascript
Python中的二叉树查找算法模块使用指南
2014/07/04 Python
总结Python中逻辑运算符的使用
2015/05/13 Python
python利用正则表达式排除集合中字符的功能示例
2017/10/10 Python
Python实现PS滤镜Fish lens图像扭曲效果示例
2018/01/29 Python
在python中获取div的文本内容并和想定结果进行对比详解
2019/01/02 Python
Django框架使用内置方法实现登录功能详解
2019/06/12 Python
简单了解python高阶函数map/reduce
2019/06/28 Python
Flask框架学习笔记之模板操作实例详解
2019/08/15 Python
Python网络编程之使用TCP方式传输文件操作示例
2019/11/01 Python
详解Python3 中的字符串格式化语法
2020/01/15 Python
pandas使用之宽表变窄表的实现
2020/04/12 Python
Python中BeautifulSoup通过查找Id获取元素信息
2020/12/07 Python
西班牙太阳镜品牌:Hawkers
2018/03/11 全球购物
会计毕业生求职简历的自我评价
2013/10/20 职场文书
销售自我评价
2013/10/22 职场文书
挂职学习心得体会
2014/09/09 职场文书
财政局个人年终总结
2015/03/03 职场文书
用Python简陋模拟n阶魔方
2021/04/17 Python
如何用python清洗文件中的数据
2021/06/18 Python
python和anaconda的区别
2022/05/06 Python