开发者社区> zchd> 正文

php关于金额比较引发的问题(转)

简介: 做电子商务的时候一般会涉及到金额的比较,按正常的思路来看用> 100) {         $bin.="1";         $int = $int - $base;         echo "1";     } else {         $bin .
+关注继续查看

做电子商务的时候一般会涉及到金额的比较,按正常的思路来看用><=这些个符号就可以了。可是要是到程序上来搞这个的话就出大事了。现在看下这段代码:

$f = 0.07;
var_dump($f * 100 == 7);//输出false

输出结果会出乎大家意料,输出false,为什么会这样呢?其实这个和电脑中存储小数的原理有关。大家都知道计算机只能存储0和1,我们日常生活习惯使用的是10进制的数据,像0.07这个小数在计算机中存储时会有精度损失,以至于计算出来的结果会有偏差。

那么怎么解决这个问题?虽然计算机存储小数有偏差,但是偏差还是非常小,像上例中0.07 * 100如果显示出小数点后面20位的话,最终的值如下

$f = 0.07;
//输出7.00000000000000088818
echo number_format($f * 100, 20)
 
可以看到已经在小数点10多位之后了。在实际中我们通常也不需要精确到后面这么多位数字,在金额方面通常精确到后面3位就好了。如果精确到小数点后面三位的话,0.07*100和7就会相等了。在php中提供了一个bccomp函数用来处理这方面的比较。
$f = 0.07;
var_dump($f * 100 == 7);
//输出0,表示两个数字精度为小数点后3位的时候相等
var_dump(bccomp($f * 100, 7, 3));
虽然最终解决了问题,但是还是想搞明白为什么0.07这样的浮点数会有精度损失,经过一段时间的研究,发现产生误差的原因:就在于浮点数的小数位在转换成二进制的时候产生的。
浮点数小数部分转换成二进制规则:乘2取整法,即每一步将十进制小数部分乘以2,所得积的小数点左边的数字(0或1)作为二进制表示法中的数字,直到满足精确度为止。
经过计算发现0.07即使计算到60位后,依然还没有结束。在计算机中,32位的计算机中浮点数尾数部分是23位,64位的是52位。所以后面多出来的部分就会被舍弃掉。
测试都是在64位机器上进行的。
$bin  "";
$int  = 7;
$base = 100;
echo "<table border='1'>";
echo "<td width='50'>位数</td>";
echo "<td width='50'>x2</td>";
echo "<td width='50'>位值</td>";
for ($i = 0; $i <= 60; $i++) {
    echo "<tr>";
    echo "<td>$i</td>";
    $int $int * 2;
    echo "<td>$int</td>";
    if ($int == 100) {
        $bin.="1";
        echo "<td>1</td>";
        break;
    }
    if ($int > 100) {
        $bin.="1";
        $int $int $base;
        echo "<td>1</td>";
    else {
        $bin .= "0";
        echo "<td>0</td>";
    }
    echo "</td>";
    echo "</tr>";
}
echo "</table>";
echo $bin;
对上例转换的二进制进行反推:
/*
  输出内容
  0.070000000000000006661338147751
  0.070000000000000006661338147751
 */
$f   = 0.0;
$bin "0001000111101011100001010001111010111000010100011110101110000";
$l   strlen($bin);
for ($i = 0; $i $l$i++) {
    if ($bin[$i] > 0) {
        $f $f + pow(2, -($i + 1));
    }
}
echo number_format($f, 30);
 
 
$f = 0.07;
echo "<br />";
echo number_format($f, 30);
 

bcadd — 将两个高精度数字相加

  bccomp — 比较两个高精度数字,返回-1, 0, 1

  bcdiv — 将两个高精度数字相除

  bcmod — 求高精度数字余数

  bcmul — 将两个高精度数字相乘

  bcpow — 求高精度数字乘方

  bcpowmod — 求高精度数字乘方求模,数论里非常常用

  bcscale — 配置默认小数点位数,相当于就是Linux bc中的”scale=”

  bcsqrt — 求高精度数字平方根

  bcsub — 将两个高精度数字相减

  整理了一些实例

  php BC高精确度函数库包含了:相加,比较,相除,相减,求余,相乘,n次方,配置默认小数点数目,求平方。这些函数在涉及到有关金钱计算时比较有用,比如电商的价格计算。

 

/**
  * 两个高精度数比较
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return int $left==$right 返回 0 | $left<$right 返回 -1 | $left>$right 返回 1
  */
var_dump(bccomp($left=4.45, $right=5.54, 2));
// -1
  
 /**
  * 两个高精度数相加
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcadd($left=1.0321456, $right=0.0243456, 2));
//1.04
 
  /**
  * 两个高精度数相减
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcsub($left=1.0321456, $right=3.0123456, 2));
//-1.98
  
 /**
  * 两个高精度数相除
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcdiv($left=6, $right=5, 2));
//1.20
 
 /**
  * 两个高精度数相乘
  * 
  * @access global
  * @param float $left
  * @param float $right
  * @param int $scale 精确到的小数点位数
  * 
  * @return string 
  */
var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2));
//7.71
 
 /**
  * 设置bc函数的小数点位数
  * 
  * @access global
  * @param int $scale 精确到的小数点位数
  * 
  * @return void 
  */ 
bcscale(3);
var_dump(bcdiv('105', '6.55957')); 
// 16.007

 

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
PHP为什么可以连接MySQL?底层原理是什么?
PHP为什么可以连接MySQL?底层原理是什么?
58 0
php为什么选mysql作为数据库?
php为什么选mysql作为数据库?
28 0
PHP操作MySQL实现增删改查的底层原理是什么?
PHP操作MySQL实现增删改查的底层原理是什么?
52 0
Docker 可以通过创建多个容器,分别部署 Linux、Nginx、MySQL 和 PHP 等组件,为什么不可以一个容器包含所有呢?底层原理是什么?
Docker 可以通过创建多个容器,分别部署 Linux、Nginx、MySQL 和 PHP 等组件,为什么不可以一个容器包含所有呢?底层原理是什么?
101 0
基于PHP和MySQL的新闻发布系统——【功能优化】
基于PHP和MySQL的新闻发布系统——【功能优化】
33697 0
基于PHP和MySQL的新闻发布系统
基于PHP和MySQL的新闻发布系统
60 0
开心档-软件开发入门之PHP - AJAX 与 MySQL
本文主要讲解AJAX 可用来与数据库进行交互式通信。
49 0
如何修复“PHP 安装缺少 WordPress 所需的 MySQL 扩展”的错误?
在WordPress建站,WordPress定制开发过程中,开发者容易遇见“您的 PHP 安装似乎缺少 WordPress 所需的 MySQL 扩展”的错误提示,如果出现这样的情况,应该怎么办?北京六翼信息有限公司的开发工程师指出,要修复错误“您的 PHP 安装似乎缺少 WordPress 所需的 MySQL 扩展”,您需要确保您的 PHP 安装已正确安装和配置 MySQL 数据库驱动程序 (mysqlnd) 和 mysqli 扩展。只有这样,您才能恢复 WordPress 和 WordPress 数据库之间的正常连接,并让您的网站重新运行。
81 0
ECS配置mySQL\MariaDB和PHP环境
ECS配置mySQL\MariaDB和PHP环境
46 0
php操作mysql防止sql注入(合集)
php操作mysql防止sql注入(合集)
170 0
+关注
zchd
架构,编程语言相关技术专家
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
PHP运行机制初探
立即下载
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
相关实验场景
更多