一、前方有坑

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. Python开发系列课程(10) - 那些年我们踩过的那些坑(上)

    那些年我们踩过的那些坑 坑01 - 整数比较的坑 在 Python 中一切都是对象,整数也是对象,在比较两个整数时有两个运算符==和is,它们的区别是: is比较的是两个整数对象的id值是否相等,也就 ...

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

    '转载地址:http://www.cnblogs.com/rwxwsblog/p/4557123.html' 这些天应朋友的要求抓取某个论坛帖子的信息,网上搜索了一下开源的爬虫资料,看了许多对于开源爬 ...

  3. Vue2.0配置mint-ui踩过的那些坑

    Vue2.0配置mint-ui踩过的那些坑 最近开发项目的时候逐渐采用vue.js+mint-ui的技术栈,但是昨天开始配置开发环境的时候,遇到了各种报错,即使是按照两家的官方文档配置,也还是会报错, ...

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

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

  5. charles都踩过哪些坑_开水果店的你,踩过了哪些坑?

    我们认为,开水果店遇到的大小问题,很多时候是有共性的.不论是开店新手还是老手,看看这里的案例,是否可以避免走一些弯路呢? 案例1 刘大飞第1次创业开水果踩过的那些坑.创业开水果店之前,刘大飞和合伙人一 ...

  6. 美团在Redis上踩过的一些坑-5.redis cluster遇到的一些问题

    转载请注明出处哈:http://carlosfu.iteye.com/blog/2254154 由于演讲时间有限,有关Redis-Cluster,演讲者没做太多介绍,简单的介绍了一些Redis-Clu ...

  7. python iocp_记对协程增加IOCP支持时候踩过的一些坑

    之前在对tbox的协程库中增加了基于IOCP的io处理,期间踩了不少的坑,这边就做个简单记录吧,省的到时候忘记了,自己看不懂自己这个代码 (= =) 坑点一 WSARecv/WSASend在lpNum ...

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

    转载自   Spring Cloud 升级最新 Finchley 版本,踩了所有的坑 Spring Boot 2.x 已经发布了很久,现在 Spring Cloud 也发布了 基于 Spring Bo ...

  9. (转)Redis上踩过的一些坑-美团

    上上周和同事(龙哥)参加了360组织的互联网技术训练营第三期,美团网的DBA负责人侯军伟给大家介绍了美团网在redis上踩得一些坑,讲的都是干货和坑. 分为5个部分: 一.周期性出现connect t ...

最新文章

  1. 优先队列的应用 C++实现
  2. Java复习二 基本数据类型与变量和常量
  3. mysql query 查询失败_如何在mysql进行查询缓存及失败的解决方法
  4. elementui tree组件层级过多时可左右滚动
  5. 【转】JNI(Java Native Interface)的简介
  6. asp.net C# 计算运算耗时时间
  7. Activity 切换 动画
  8. 计算机网站之TCP报文结构
  9. 哈工大本部2022形式语言与自动机期末试题
  10. honeyview(蜂蜜浏览器)将图片置于顶层
  11. 和跨域CORS有关的几个请求头和响应头请求头
  12. Windows注册表知识
  13. 耕作方式对微生物群落的影响
  14. 图片编辑软件有哪些?推荐几款好用的专业工具
  15. 反射(三) 笔记
  16. OpenWrt -【记录】群辉NAS上安装软路由
  17. 黑马程序员---面向对象上(封装,继承,多态)
  18. U-Net网络结构详解
  19. vr虚拟现实技术的前景!对未来发展带来有利的趋势吗?
  20. 手机腾讯网前端框架MT2.1.0发布

热门文章

  1. 恒指期货投资投资者如何稳步获利
  2. 【python 手机号码归属地】手机号码归属地获取
  3. 南邮计算机控制系统性能分析实验报告,南京邮电大学计算机组成原理考前复习...
  4. 编程语言专题《开发篇,C语言强制类型转换本身值不发生改变》
  5. 电脑设置定时播放任务
  6. 在不同的软件中截图的快捷方式
  7. QQ浏览器兼容模式下Cookie失效 导致的NetCore Cookie认证失效
  8. Python 函数的了解与使用
  9. 【教程】Spire.Doc系列教程(1):给Word文档设置背景颜色和背景图片
  10. 机器学习——PCA降维