福州网站建设>网站新闻>php技术

踩坑之json_encode精度丢失问题

发布日期:2022-01-12浏览次数:179 来源:福州网站建设

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
php技术有关的文章
如果您有什么问题,欢迎咨询我们客服! 点击QQ咨询
欧美肥老太牲交大战