zoukankan      html  css  js  c++  java
  • php踩过的那些坑(5)浮点数计算

    一、前方有坑

    php在使用加减乘除等运算符计算浮点数的时候,经常会出现意想不到的结果,特别是关于财务数据方面的计算,给不少工程师惹了很多的麻烦。比如今天工作终于到的一个案例:

    $a = 2586;

    $b = 2585.98;

    var_dump($a-$b);

    期望的结果是:float(0.02)

    实际结果:

    float(0.019999999999982)

    php踩过的那些坑(1)浮点数计算

    人生有坑,处处提防

    二、防坑攻略:

    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 函数

  • 相关阅读:
    java中的数组
    java中的break continue
    java 自加和短路问题 几个例子
    循环语句
    K-近邻算法小结
    数据结构与算法学习笔记 (三) 排序 搜索
    数据结构与算法学习笔记 (二) 栈 链表 队列 树 堆 图 并查集
    Spark数据分析技术学习笔记(一)——基础概念、RDD使用
    Python数据分析学习笔记
    Python机器学习(Sebastian著 ) 学习笔记——第六章模型评估与参数调优实战(Windows Spyder Python 3.6)
  • 原文地址:https://www.cnblogs.com/phpfensi/p/8143367.html
Copyright © 2011-2022 走看看