博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
php-浮点数计算,double类型数加减乘除必须用PHP提供的高精度计算函数
阅读量:6829 次
发布时间:2019-06-26

本文共 1716 字,大约阅读时间需要 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 函数

转载于:https://www.cnblogs.com/kenshinobiy/p/10797902.html

你可能感兴趣的文章
php程序在浏览器哪里判断,一个判断PHP程序是否被同时在不同浏览器上执行的问题...
查看>>
php 获取5分钟前,php时间轴开发,即显示为“刚刚”、“5分钟前”、“昨天12:10...
查看>>
php ob_end_clean(),ob_end_clean(): failed to delete buffer-ThinkPHP 5.1.23
查看>>
ecshop send.php on line 71,ECSHOP调用指定DEDECMS织梦栏目文章列表
查看>>
linux 怎么运行多个target,radhat7下使用targetcli为多个启动器配置iSCSI访问控制
查看>>
c语言2015实验答案,2015年计算机二级《C语言》精选练习题及答案(15)
查看>>
c语言中word16,(word)16道嵌入式C语言面试题.doc
查看>>
c语言雷克子波,vans.c · skateboi/雷克子波与一维数组卷积的图像 - Gitee.com
查看>>
c语言ctrl c线程中动态申请的内存,【C++】 外传篇 3_动态内存申请的结果
查看>>
android textview显示不下,Android TextView不显示我的所有文本
查看>>
android手机之间tcp通信,Android 设备间通信方式
查看>>
数据库集群系统研究系列(2)-现存的数据库的解决方案的原理解析
查看>>
Racktables自定义报表
查看>>
网摘是否影响网络推广和SEO
查看>>
Outlook中轻松添加LDAP服务
查看>>
统一沟通-技巧-2-Lync 2010-照片-无显示-组织-显示-为-自己
查看>>
Exchange企业实战技巧(24)OWA常用操作技巧
查看>>
Python脚本监控线上AMQ Number of Pending Messages数量
查看>>
TX Text Control文字处理教程(11)使用文本框
查看>>
考前熟记的知识点(3)_《系统集成项目管理工程师软考辅导》
查看>>