moozik

php的坑,浮点数相关,与bccomp函数
官方介绍:http://www.php.net/manual/en/function.bccomp.php中文介绍...
扫描右侧二维码阅读全文
22
2016/11

php的坑,浮点数相关,与bccomp函数

官方介绍:http://www.php.net/manual/en/function.bccomp.php

中文介绍:http://php.freehostingguru.com/function.php-bccomp.php


bccomp

比较二个高精确度数字。

语法: int bccomp(string left operand, string right operand, int [scale]);

返回值: 整数

函数种类: 数学运算

此函数比较二个高精确度的数字。输入二个字符串,若二个字符串一样大则返回 0;若左边的数字字符串 (left operand) 比右边 (right operand) 的大则返回 +1;若左边的数字字符串比右边的小则返回 -1。scale 是一个可有可无的选项,表示返回值的小数点后所需的位数。


经过测试,前两个参数不管是字符还是浮点数都可以正常比对。使用== 或者 === 均不能比对浮点数,会出现时对时错的情况。谨记。


20161128:

echo bccomp(123.1234,123.1235,3);

会返回0,表示想等,bccomp不会四舍五入。


2016-11-24 更新 (字符串比对的坑)

$a='70001502570002755697';
$b='70001502570002759229';

if($a==$b){
    echo 1;
}else{
    echo 2;
}

以上的代码返回的结果将是“1”,这是由于php将数字类型字字符串转换为数字类型,而int类型是有精度的,经过测试,精度为16位。

如果去掉一位数字,改成以下代码,将会输出“2”,因为第16位变为不等了。

$a='0001502570002755697';
$b='0001502570002759229';

if($a==$b){
    echo 1;
}else{
    echo 2;
}

学艺不精,耽误了好多时间,解决问题的方法是使用

strcmp(string1,string2)
strcasecmp(string1,string2)

前者大小写敏感,后者大小写不敏感。


2016-11-26 更新

使用phpexcel读取excel账单时,读取的浮点数会产生异常情况

$item = array(
	'bill_id' => $currentSheet->getCell("A".$line)->getValue(),//网关接入号
	'bill_type' => $currentSheet->getCell("H".$line)->getValue(),//交易状态 成功 失败 。退款类型:部分退款,全额退款
	'bill_time' => date("Y-m-d H:i:s", substr(PHPExcel_Shared_Date::ExcelToPHP($currentSheet->getCell("M".$line)->getValue()),0,19)),//交易时间
	'gleepay_id' => $currentSheet->getCell("B".$line)->getValue(),
	'payment_id' => $currentSheet->getCell("C".$line)->getValue(),
	'order_time' => date("Y-m-d H:i:s", substr(PHPExcel_Shared_Date::ExcelToPHP($currentSheet->getCell("N".$line)->getValue()),0,19)),//处理时间
	'purchase_curr' => $currentSheet->getCell("D".$line)->getValue(),
	'purchase_amount' => $currentSheet->getCell("E".$line)->getValue(),
	'checkout_curr' => $currentSheet->getCell("F".$line)->getValue(),
	'checkout_amount' => $currentSheet->getCell("G".$line)->getValue(),
	'bill_amount' => '0', //订单金额
	'business' => '0' //1:交易。0:退款。
);

其中交易金额和结单金额等数字类型金额在读取时,由于二进制无法准确存储92.10类型的数字,所以会产生精度误差,读取的结果是这样的。

结果:
第14行:交易金额 整数位数不大于9位,小数位数不大于3位;92.09999999999999
第16行:交易金额 整数位数不大于9位,小数位数不大于3位;92.09999999999999结单金额 整数位数不大于9位,小数位数不大于3位;92.09999999999999入账金额 整数位数不大于9位,小数位数不大于3位;92.09999999999999
第18行:交易金额 整数位数不大于9位,小数位数不大于3位;89.40000000000001结单金额 整数位数不大于9位,小数位数不大于3位;89.40000000000001入账金额 整数位数不大于9位,小数位数不大于3位;89.40000000000001

但是以上问题只在linux环境下会出现,在windows本机上测试并不会出现,也许和php版本有关,暂未探究。

第14行的交易金额是手动输入的92.10,这就排除了由于excel存储的数值本来就是超长小数的情况,问题可能是由于excel读取的问题或者php存储浮点数的问题,解决办法可以使用四舍五入来解决,但是请慎用。


2016-11-28 更新

经测试发现,当在服务器目录中放入下面代码,输出的数字并没有上述情况,怀疑是由于框架模板。

$item = array(
    89.4,
    92.1
);

var_dump($item);

可是在渲染html时传递的数组并没有这样的情况。

		$this->render('GL0010',
			array(
				'a'=>array(
						'a'=>92.1,
						'b'=>89.4
					)
			)
		);

页面显示如下:

array(2) { ["a"]=> float(92.1) ["b"]=> float(89.4) }


于是多次尝试问题原因,最后发现,在引入phpexcel之前,也就是执行下面代码之前,都是正常的。

include_once $url.'/protected/extensions/phpexcel/PHPExcel/IOFactory.php';

测试了,在这段代码之前定义空数组,然后在他下面使用角标重新定义空数组,使之其中含有92.1和89.4这两个数字,结果发现,不再报错,说明当引入phpexcel之后,对数组的创建产生了未知的影响,导致本来不存在精度问题的数字,出现异常。


最后这里的数字我使用number_format()函数解决。

$item['purchase_amount'] = number_format($item['purchase_amount'],3,'.','');

由于excel中的金额为保留2位小数,所以这里截取3位小数四舍五入可以保证金额精确,并且使千分位的字符为空,这样就得到了字符类型的数字金额,我的目的为导入数据库,至此任务完成。

最后修改:2017 年 03 月 26 日 06 : 46 PM

发表评论