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
以上是由福州网站建设的小编为你分享了"踩坑之json_encode精度丢失问题"文章,如果你在这方面有什么问题,随时联系我们