一、前方有坑

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)浮点数计算的更多相关文章

  1. 三分之一的程序猿之社交类app踩过的那些坑

    三分之一的程序猿之社交类app踩过的那些坑 万众创新,全民创业.哪怕去年陌生人社交不管融资与否都倒闭了不知道多少家,但是依然有很多陌生人社交应用层出不穷的冒出来.各种脑洞大开,让人拍案叫起. 下面我们 ...

  2. 【Fine原创】JMeter分布式测试中踩过的那些坑

    最近因为项目需要,研究了性能测试的相关内容,并且最终选用了jmeter这一轻量级开源工具.因为一直使用jmeter的GUI模式进行脚本设计,到测试执行阶段工具本身对资源的过量消耗给性能测试带来了瓶颈, ...

  3. 与webview打交道中踩过的那些坑

    随着HTML5被越来越多的用到web APP的开发当中,webview这一个神器便日渐凸显出重要地位.简要的说,webview能够在移动应用中开辟出一个窗口,在里面显示html页面,css以及js代码 ...

  4. 美团在Redis上踩过的一些坑-目录(本人非美团)(转)

    来自:http://carlosfu.iteye.com/blog/2254154 分为5个部分:    一.周期性出现connect timeout    二.redis bgrewriteaof问 ...

  5. 安装python爬虫scrapy踩过的那些坑和编程外的思考

    这些天应朋友的要求抓取某个论坛帖子的信息,网上搜索了一下开源的爬虫资料,看了许多对于开源爬虫的比较发现开源爬虫scrapy比较好用.但是以前一直用的java和php,对python不熟悉,于是花一天时 ...

  6. [原创]阿里云RocketMQ踩过的哪些坑

    由于公司的最近开始使用RocketMQ来做支付业务处理, 便开启了学习阿里云RocketMQ的学习与实践之路, 其中踩了不少的坑, 大部份是由于没有仔细查看阿里云的技术文档而踩的坑. 但是有一个非常大 ...

  7. Spring Cloud 升级最新 Finchley 版本,踩了所有的坑!

    Spring Boot 2.x 已经发布了很久,现在 Spring Cloud 也发布了 基于 Spring Boot 2.x 的 Finchley 版本,现在一起为项目做一次整体框架升级. 升级前 ...

  8. Redis上踩过的一些坑

    来自: http://blog.csdn.net//chenleixing/article/details/50530419 上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DB ...

  9. Android Camera开发经验总结以及踩过的那些坑

    写在开头 需求方:上传试卷的时候,用户自己拍的照片有很多问题.如:不清晰.图片歪了.错误图片等.我们要是能够对拍摄照片进行识别处理就好了,能够裁切矫正就更好了,最好可以像二维码扫描一样,直接识别处理- ...

随机推荐

  1. 学习笔记24_MVC前后台数据交互

    *最普通的交互方式,在Contoller中的Action方法内 public ActionResult Index() { ViewData["Key"] =Value; Retu ...

  2. 伪紫题p5194 天平(dfs剪枝)

    这题作为一个紫题实在是过分了吧...绿的了不起了.—————————————————————————— 看题第一眼,01背包无误.2min打好一交全屏紫色(所以这就是这题是紫色的原因233?) re原 ...

  3. Fiddler 原理及iPhone的配置

    原理: 首先Fiddler运行在自己的PC上,Fiddler运行的时候会在PC的8888端口开启一个代理服务,这个服务实际上是一个HTTP/HTTPS的代理. 确保手机和PC在同一个局域网内,我们可以 ...

  4. 关于Struts.xml的路径配置以及对应的连接路径问题

    主要记住一点!: 1.   namespace的路径配置例如:/x:   2.  name名自由配置(主要用来是做action跳转方法用的) 3.  form表单中提交action时以这个格式提交(此 ...

  5. Resource Path Location Type Target runtime Apache Tomcat v6.0 is not defined(项目报错)已解决

    我换了开发工具后,导入的项目不是这里报错就是那里不错.不过,我喜欢.在tomcat里面部署项目后,定位到报错行时,总是提示我这句话:Description Resource Path Location ...

  6. GitHub和Git

    GitHub托管项目代码 首先一些基本概念: repository(仓库) 用来存放项目代码,每个项目代表一个仓库,开一个项目就意味着你有一个仓库. star(收藏) 收藏方便下次查找. fork(复 ...

  7. mysql基础之数据类型

    一.整型 分为:tinyint .smallint .mediumint .int .bigint 常用的 分为以下三项: tinyint. smallint.int 数据类型 存储范围 字节 tin ...

  8. gdb(ddd,kdevelop等)调试ZeroIce开发的应用程序,中断信号引起的问题

    不作文,只记要点. 1.Ice::Application的程序框架默认对SIGHUP, SIGINT, SIGTERM进行处理.目的就是捕捉Ctrl+C发出信号有序地结束程序.这个功能扰乱了我们使用g ...

  9. vue项目中使用百度统计

    统计有多少人访问了自己的网站(wap端pc web端都适用),或者更细的统计网站每个页面的访问量,可以使用百度统计 百度统计传送门 按提示注册登录即可 登录后-->管理-->新增网站,配置 ...

  10. PL真有意思(四):控制流

    前言 对大多数计算模型而言,顺序都是基本的东西,它确定了为完成所期望的某种工作,什么事情应该最先做,什么事应该随后做,我们可以将语言规定顺序的机制分为几个类别: 顺序执行 选择 迭代 过程抽象 递归 ...