一、前方有坑

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-浮点数计算,double类型数加减乘除必须用PHP提供的高精度计算函数的更多相关文章

  1. BigDecimal精确计算工具类

    前言 在实际开发中,遇到例如货币,统计等商业计算的时候,一般需要采用java.math.BigDecimal类来进行精确计算.而这类操作通常都是可预知的,也就是通用的.所以,写了个工具类来方便以后的工 ...

  2. java 金额计算,商业计算 double不精确问题 BigDecimal,Double保留两位小数方法

    解决办法================== http://blog.javaxxz.com/?p=763 一提到Java里面的商业计算,我们都知道不能用float和double,因为他们无法 进行精 ...

  3. 学以致用:手把手教你撸一个工具库并打包发布,顺便解决JS浮点数计算精度问题

    本文讲解的是怎么实现一个工具库并打包发布到npm给大家使用.本文实现的工具是一个分数计算器,大家考虑如下情况: \[ \sqrt{(((\frac{1}{3}+3.5)*\frac{2}{9}-\fr ...

  4. Java 高精度浮点数计算工具

    说起编程中的高精度数值,我第一反应就是double类型了.的确,double阶码11位,尾数52位,几乎能应对任何苛刻的要求......然而,当我天真地尝试用double来算泰勒展开式的函数值,离散代 ...

  5. 金额的计算BigDecimal类

    金额的计算BigDecimal类 double d = 9.84; double d2 = 1.22; //注意需要使用BigDecimal(String val)构造方法 BigDecimal bi ...

  6. 关于js浮点数计算精度不准确问题的解决办法

    今天在计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前就一直碰到这个问题,都是简单的使用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的.因此在网上收集了一些处理浮点数精 ...

  7. java第二周的学习知识4(对原码,补码,反码和java中浮点数计算不准确的总结)

    原码:一个正数,转换为二进制位就是这个正数的原码.负数的绝对值转换成二进制位然后在高位补1就是这个负数的原码. 但是原码有几个缺点,零分两种 +0 和 -0 .很奇怪是吧!还有,在进行不同符号的加法运 ...

  8. float类型数保留一位小数

    float类型数保留一位小数 float a = 2.5f; float b = 1.2f; System.out.println(a/b); System.out.println((float)(M ...

  9. jmeter 中 浮点数计算精度问题

    jmeter 中 浮点数计算精度问题解决方法: 编写 beanshell 时使用 java.math.BigDecimal 方法构造,使用 BigDecimal 并且一定要用 String 来够造. ...

随机推荐

  1. [LeetCode] 63. 不同路径 II ☆☆☆(动态规划)

    描述 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为“Finish”). 现在 ...

  2. Linux网卡上添加多个IP

    1.查看现有网卡ip addr show看到ens_xxx 2.进入网卡配置项cd /etc/sysconfig/network-scripts/vim ifcfg-ens192末尾添加2行:IPAD ...

  3. MySQL Backup--Xtrabackup备份异常(MySQL Server has gone away)

    错误场景MySQL 版本:5.7.24XtraBackup 版本:2.4.8CentOS 版本:6.5 MySQL需要新增一个从库,为避免XtraBackup备份影响生产,选择在从库上进行备份,备份脚 ...

  4. Linux正则表达式,grep总结,sed用法

    原文: 1.sed   流编辑器,实现对文字的增删改替换查(过滤.取行),能同时处理多个文件多行的内容,可以不对原文件改动,把整个文件 输入到屏幕,可以把只匹配到模式的内容输入到屏幕上.还可以对原文件 ...

  5. Metasploit3

    1.之前使用的版本是Metasploit2的版本操作系统是基于Ubuntu的,渗透测测试模块也是基于metasploit的,基本上没有设置,Metasploitable3添加了很多安全机制 ,防火墙和 ...

  6. ts介绍

    typescript是微软开发的一门编程语言,它是javascrip的一个超集,它遵循最新的es6脚本语言规范,typescript扩展了javascript的语法,任何已经存在的javascipt程 ...

  7. 让 Python 代码更易维护的七种武器——代码风格(pylint、Flake8、Isort、Autopep8、Yapf、Black)测试覆盖率(Coverage)CI(JK)

    让 Python 代码更易维护的七种武器 2018/09/29 · 基础知识 · 武器 原文出处: Jeff Triplett   译文出处:linux中国-Hank Chow    检查你的代码的质 ...

  8. 怎么保证redis集群的高并发和高可用的?

    redis不支持高并发的瓶颈在哪里? 单机.单机版的redis支持上万到几万的QPS不等. 主要根据你的业务操作的复杂性,redis提供了很多复杂的操作,lua脚本. 2.如果redis要支撑超过10 ...

  9. 性能优化处理CPU快慢问题

    经常听到有人说磁盘很慢.网络很卡,这都是站在人类的感知维度去表述的,比如拷贝一个文件到硬盘需要几分钟到几十分钟,够我去吃个饭啦:而从网络下载一部电影,有时候需要几个小时,我都可以睡一觉了. 最为我们熟 ...

  10. 「SDOI2016」征途

    征途 Pine开始了从S地到T地的征途. 从S地到T地的路可以划分成\(n\)段,相邻两段路的分界点设有休息站. Pine计划用\(m\)天到达T地.除第\(m\)天外,每一天晚上Pine都必须在休息 ...