今天遇到微信H5支付出现异常,而且只有一个商品出现异常,其他商品都可以正常调起微信H5支付,这个就是比较奇怪了,所以去到代码里去寻找问题。
初步发现是,这个商品无法成功获取到微信H5的支付链接。
打印这个$headers,发现报如下错误:提示需要一个合法的64位有符号整数
仔细看下,发现value的值出现问题了,本来是整数的,这里成了浮点数,明显出现了精度丢失的情况。
再看代码,发现原本是整数值的$payparm参数,经过json_encode($payparm)后,出现了精度丢失的问题。
这里的解决办法是,将$payparm中的整数参数,使用intval()再强制转换一下。例如 $value = intval($value);
随后又搜索了下 json_encode() 中数字精度丢失问题,发现有许多不错的解决办法,这里搬来保存一下。
目录
1.前言
2.案例演示
3.解决方法
3.1方法1:强行转换成字符串保证精度
3.2方法2:格式化数字number_format函数
3.3方法3:修改配置项serialize_precision
3.4知识点补充
3.5其他取整函数
4.bcsub()函数精度相减
1.前言
最近在工作中,后台出现了这样的错误,传递的手续费是29块钱,但是传递到了第三方接口就报错了,查看日志显示该手续费变成了28.999999999999996,日志如下:
明明是29怎么就变了?通过断点检测,原来是json_encode()转换的问题
2.案例演示
$param={
‘amount’=>5000,
‘fee’=>29
}
var_dump(json_encode($param));//{"amount":5000,"fee":28.999999999999999999996}
3.解决方法
3.1方法1:强行转换成字符串保证精度
$request['param']['feeAmount']=(string)$request['param']['feeAmount'];
注意:使用这种方法千万要注意,对接接口是否有变量类型要求
3.2方法2:格式化数字number_format函数
number_format(number,decimals,decimalpoint,separator)
参数:
number参数是要格式化的数据
decimals参数是保留的小数
decimalpoint参数是规定用作小数点的字符串
separator参数是规定用作千位分隔符的字符串
案例:
$request['param']['feeAmount']=(int)number_format($request['param']['feeAmount'],0);
注意:number_format返回的是字符串string,要注意接口是否有规范要求,如果有,则须强行转换为int或接口规范的类型
疑问1:这时候应该有人在想能不能直接强制转换为int呢?注意,float强制转换成int有坑!
答:int类型是向下取整的,比如:12910.9 会被转换为 12910
疑问2:这时就会有人问我,浮点数显示的是8,为什么转换成整数会变成7?
答:floor((0.1+0.7)*10),其结果是7而不是8,是因为该结果内部表示的是7.9999.....所以不要相信浮点数结果精确,也不要比较两个浮点数是否相等
3.3方法3:修改配置项serialize_precision
json_encode() 转换浮点小数溢出现象只出现在PHP 7.1+版本,是因为php源码对于json_encode()转换使用到了serialize_precision配置项,如下图
static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
{
size_t len;
char num[PHP_DOUBLE_MAX_LENGTH];
php_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
len = strlen(num);
if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < PHP_DOUBLE_MAX_LENGTH - 2) {
num[len++] = '.';
num[len++] = '0';
num[len] = '\0';
}
smart_str_appendl(buf, num, len);
}
关于PHP函数serialize_precision (integer)的一些概念了解:
适用范围:PHP_INI_ALL;
默认值:100
serialize_precision指令的数量决定了双打和彩车被序列化后的浮点数字存储。设置到一个合适的值,确保精度的数字时,可能丢失以后反序列化。所以我们要使得json_encode()转换浮点数没有小数溢出,建议使用默认值 serialize_precision = -1 即可 。
3.4知识点补充
json_encode有个选项JSON_PRESERVE_ZERO_FRACTION,表示如果是个整数, 是否保留小数点和尾数0,举例如下:
<?php
echo json_encode(223.0);// 223
echo json_encode(223.0, JSON_PRESERVE_ZERO_FRACTION);// 223.0
3.5其他取整函数
四舍五入取整 round(param)
向上取整 ceil(param)
向下取整 floor(param)
4.bcsub()函数精度相减
格式:
string bcsub ( string $left_operand , string $right_operand [, int $scale = int ] )
说明:
2个任意精度数字的减法
参数:
left_operand:字符串类型的左操作数.
right_operand:字符串类型的右操作数.
scale:此可选参数用于设置结果中小数点后的小数位数。也可通过使用 bcscale() 来设置全局默认的小数位数,用于所有函数。
返回值:
返回减法之后结果为字符串类型.
代码案例:
<?php
$a = '1.234' ;
$b = '5' ;
echo bcsub ( $a , $b ); // -3
echo bcsub ( $a , $b , 4 ); // -3.7660
————————————————
版权声明:本文为CSDN博主「拯救世界的派大星」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_46266407/article/details/105556444