php踩过的那些坑(5)浮点数计算
一、前方有坑
php在使用加减乘除等运算符计算浮点数的时候,经常会出现意想不到的结果,特别是关于财务数据方面的计算,给不少工程师惹了很多的麻烦。比如今天工作终于到的一个案例:
$a = 2586;
$b = 2585.98;
var_dump($a-$b);
期望的结果是:float(0.02)
实际结果:
float(0.019999999999982)
人生有坑,处处提防
二、防坑攻略:
1、通过乘100的方式转化为整数加减,然后在除以100转化回来……
2、使用number_format转化成字符串,然后在使用(float)强转回来……
3、php提供了高精度计算的函数库,实际上就是为了解决这个浮点数计算问题而生的。
主要函数有:
bcadd — 将两个高精度数字相加
bccomp — 比较两个高精度数字,返回-1, 0, 1
bcdiv — 将两个高精度数字相除
bcmod — 求高精度数字余数
bcmul — 将两个高精度数字相乘
bcpow — 求高精度数字乘方
bcpowmod — 求高精度数字乘方求模,数论里非常常用
bcscale — 配置默认小数点位数,相当于就是Linux bc中的”scale=”
bcsqrt — 求高精度数字平方根
bcsub — 将两个高精度数字相减
前两种流氓的办法就不测试了,使用bcsub测试第三种两数相减的例子,
先看bcsub用法(来自官网)
string bcsub ( string $left_operand , string $right_operand [, int $scale = int ] )
参数
left_operand 字符串类型的左操作数.
right_operand 字符串类型的右操作数.
scale 此可选参数用于设置结果中小数点后的小数位数。也可通过使用 bcscale() 来设置全局默认的小数位数,用于所有函数。
返回值 返回减法之后结果为字符串类型.
测试代码:
var_dump(bcsub($a,$b,2));
结果
0.02
其他的函数请参考PHP官方网站
三、为啥有坑:
php的bug?不是,这是所有语言基本上都会遇到的问题,所以基本上大部分语言都提供了精准计算的类库或函数库。
要搞明白这个原因, 首先我们要知道浮点数的表示(IEEE 754):
浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11指数位(Q), 52位尾数(M)表示(一共64位).
符号位:最高位表示数据的正负,0表示正数,1表示负数。
指数位:表示数据以2为底的幂,指数采用偏移码表示
尾数:表示数据小数点后的有效数字.
这里的关键点就在于, 小数在二进制的表示, 小数如何转化为二进制呢?
算法是乘以2直到没有了小数为止。这里举个例子,0.9表示成二进制数
0.9*2=1.8 取整数部分 1
0.8(1.8的小数部分)*2=1.6 取整数部分 1
0.6*2=1.2 取整数部分 1
0.2*2=0.4 取整数部分 0
0.4*2=0.8 取整数部分 0
0.8*2=1.6 取整数部分 1
0.6*2=1.2 取整数部分 0
.........
0.9二进制表示为(从上往下): 1100100100100......
注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了"减不尽"的精度丢失问题。
换句话说:我们看到十进制小数,在计算机内存储的不是一个精确的数字,也不可能精确。所以在数字加减乘除后出现意想不到的结果。
四、防坑提示
基于以上原因,所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。如果确实需要更高的精度,应该使用任意精度数学函数或者 gmp 函数
php踩过的那些坑(5)浮点数计算的更多相关文章
- 三分之一的程序猿之社交类app踩过的那些坑
三分之一的程序猿之社交类app踩过的那些坑 万众创新,全民创业.哪怕去年陌生人社交不管融资与否都倒闭了不知道多少家,但是依然有很多陌生人社交应用层出不穷的冒出来.各种脑洞大开,让人拍案叫起. 下面我们 ...
- 【Fine原创】JMeter分布式测试中踩过的那些坑
最近因为项目需要,研究了性能测试的相关内容,并且最终选用了jmeter这一轻量级开源工具.因为一直使用jmeter的GUI模式进行脚本设计,到测试执行阶段工具本身对资源的过量消耗给性能测试带来了瓶颈, ...
- 与webview打交道中踩过的那些坑
随着HTML5被越来越多的用到web APP的开发当中,webview这一个神器便日渐凸显出重要地位.简要的说,webview能够在移动应用中开辟出一个窗口,在里面显示html页面,css以及js代码 ...
- 美团在Redis上踩过的一些坑-目录(本人非美团)(转)
来自:http://carlosfu.iteye.com/blog/2254154 分为5个部分: 一.周期性出现connect timeout 二.redis bgrewriteaof问 ...
- 安装python爬虫scrapy踩过的那些坑和编程外的思考
这些天应朋友的要求抓取某个论坛帖子的信息,网上搜索了一下开源的爬虫资料,看了许多对于开源爬虫的比较发现开源爬虫scrapy比较好用.但是以前一直用的java和php,对python不熟悉,于是花一天时 ...
- [原创]阿里云RocketMQ踩过的哪些坑
由于公司的最近开始使用RocketMQ来做支付业务处理, 便开启了学习阿里云RocketMQ的学习与实践之路, 其中踩了不少的坑, 大部份是由于没有仔细查看阿里云的技术文档而踩的坑. 但是有一个非常大 ...
- Spring Cloud 升级最新 Finchley 版本,踩了所有的坑!
Spring Boot 2.x 已经发布了很久,现在 Spring Cloud 也发布了 基于 Spring Boot 2.x 的 Finchley 版本,现在一起为项目做一次整体框架升级. 升级前 ...
- Redis上踩过的一些坑
来自: http://blog.csdn.net//chenleixing/article/details/50530419 上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DB ...
- Android Camera开发经验总结以及踩过的那些坑
写在开头 需求方:上传试卷的时候,用户自己拍的照片有很多问题.如:不清晰.图片歪了.错误图片等.我们要是能够对拍摄照片进行识别处理就好了,能够裁切矫正就更好了,最好可以像二维码扫描一样,直接识别处理- ...
随机推荐
- 学习笔记47_关于Session局限性问题,Memcache
三大问题: 1.Session性能问题 2.不能稳定输出.考虑使用进程外Session 3.组成集群,登录数据进行共享 (比如说像百度,百度网盘,百度文库等是使用不同的服务机器的,怎样避免使用的时候不 ...
- CSPS模拟 97
越来越弱了, 而且不如之前积极了,非常的颓废. 联赛前出现这种问题也是够我受的. T1 打表找规律失败.jpg 一直在找多项式规律,没找指数规律. T2 唉. T3 考虑到最多有n种颜色,考虑在线段树 ...
- 命运Ⅰ&命运Ⅱ
upd:为啥下面的相关博文都是各种退役记(这TM怎么就相关了) 竟然被卡线了,16名,我这几次考试也是炸到了一定境界了... 前三次模拟总榜rk1,第一次分机房rk4,第二次分机房rk11,第三次分机 ...
- Git连接GitHub仓库详解
[Annotation]本文将从标题八开始,因为前七个标题是关于Git的基本操作,如果对Git的基本操作不了解的话,可以点击下方链接先看一下Git怎么使用. 关于Git的详细使用 八:创建SSH Ke ...
- 【集合系列】- 深入浅出分析Collection中的List接口
一.List简介 List 的数据结构就是一个序列,存储内容时直接在内存中开辟一块连续的空间,然后将空间地址与索引对应. 以下是List集合简易架构图 由图中的继承关系,可以知道,ArrayList. ...
- [java笔记] 最近学的一些笔记
1.@Override的用法 2.父类的返回值类型的范围,与子类返回值类型的返回的大小关系: 3.子类方法的权限修饰符,与子类方法的权限修饰符: 4.如果p1是一个对象,p2也是个对象,那么代码p1= ...
- ubantu14.04安装storm伪分布式
1.安装jdk 安装:sudo apt-get install openjdk-7-jdk 配置: 修改文件 sudo nano /etc/profile , 添加以下内容: 立即执行使之生效: 2. ...
- MySQL数据库的10大经典错误案例
学习任何一门技术的同时,其实就是自我修炼的过程.沉下心,尝试去拥抱数据的世界! 案例一 Too many connections (连接数过多,导致连接不上数据库,业务无法正常进行) 问题还原: 解决 ...
- 微擎JS资源请求 403
微擎JS资源请求 403 1.确认JS是否指定 type ==> text/javascript 2.确认src的路径是否正确,{MODULE_URL}项目的根目录带反斜杠 3.实例:(PS:t ...
- django_4数据库2——表外键
表关系: many to one many to many one to one many to one 记录是如何创建出来的? 先创建one,在在创建many,创建时加入ForeignKe ...