资讯专栏INFORMATION COLUMN

PHP7 serialize_precision 配置不当导致 json_encode() 浮点小数

zhangyucha0 / 2480人阅读

摘要:感谢地狱星星原因已找到该现象只出现在版本上建议使用默认值即可参考事情是这样

感谢 @地狱星星:
原因已找到, 该现象只出现在PHP 7.1+版本上
建议使用默认值 serialize_precision = -1 即可
参考: https://wiki.php.net/rfc/prec...
----------------------------------------------------------------------------------

事情是这样的,项目里发现一个奇怪的现象,json_encode一个带浮点价格的数据, 出现溢出, 比如:


这明显是不能接受的, 数据虽然很接近, 但毕竟已经变更了
下意识地认为这是php的一个bug, 不能准确地json序列化一个浮点小数
这个问题google了半天竟然也无果, 然后我跟同事说, 你们json_encode数据里有小数的时候, 记得先number_format()转化成字符串.

自己又写了个fix方法, 遍历数据, 把is_float()的数据都用number_format()转化了

function json_encode_pre($d, $depth=128, $level=0){
    if($level>$depth) return $d;
    if(is_array($d)){
        foreach ($d as $i => $v) { $d[$i] = json_encode_pre($v, $depth, $level+1); }
        return $d;
    }
    if(is_float($d)){
        # 测试发现number_format有效数字低于18(保守取16)时,不会溢出
        $p = 16 - strlen(intval($d));
        $f = number_format($d, $p);
        if($p>1){ $f = preg_replace("/0+$/","", $d); }
        return $d;
    }
    return $d;
}

echo number_format(277.2, 14); // 当18位有效数字处理(277.2已有4位有效数字)
// 277.199999999999989
echo number_format(277.2, 12); // 当16位
// 277.2000000000000
echo json_encode(json_encode_pre(277.2));
// "277.2"

看到这结果时, 便猜测是有效数字位数的问题导致了溢出, PHP怎么会有这么蠢的设计呢?这是我当时的想法.

然后想着是不是能从源码下手, 于是查了下php源码, 发现这段代码:

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] = "";
    }
    smart_str_appendl(buf, num, len);
}

是的, 竟然发现了配置项serialize_precision, 这个配置我当初的理解是serialize()方法用的, 而我当初尝试修改过配置项precision, 然并卵, 原来json_encode会用到serialize_precision, 于是修改php.ini, 把它设为16, 原来是17还是18忘了.


如果你想重现我的问题, 很简单, 把serialize_precision设为20或更大, 至于这个有效数字最大值是多少才不会溢出, 我还不知道跟什么有关系, 希望能有大神指点了.
我是通过改配置数值, 出现溢出的值再减去2来用的.

附上配置项说明, 从官方说明上看, 很难想像是跟json_encode是有关系的.

; php.ini

; When floats & doubles are serialized store serialize_precision significant
; digits after the floating point. The default value ensures that when floats
; are decoded with unserialize, the data will remain the same.
serialize_precision = 16

; The number of significant digits displayed in floating point numbers.
; http://php.net/precision
precision = 16

另外源码里有个json_encode的选项JSON_PRESERVE_ZERO_FRACTION, 这个的意思是如果是个是个整数, 是否保留小数点和0, 来看测试结果:

           
               
                                           
                       
                 

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22540.html

相关文章

  • 修复 PHP7.1、7.2beta 中 JSON_encode () 处理 float/double

    摘要:在中,使用函数处理型数值时会出现溢出。网上一般认为是由于中项中配置不当引起,但实际际修改此配置项并不能解决问题。示例用法修复精度并进行只获取修复后的数据,不进行安装安装包文件配置安装新包后会默认执行命令,所以可以不进行下面的操作。 在 PHP7.1、7.2beta 中,使用 json_encode() 函数处理 float/double 型数值时会出现溢出。 网上一般认为是由于 ph...

    Channe 评论0 收藏0
  • 基于Swoole和Redis实现的并发队列处理系统

    摘要:大家知道,一个消息队列处理系统主要分为两大部分消费者和生产者。任务系统实时的对任务队列进行,出来一个任务就一个子进程,由子进程完成具体的任务逻辑。新的设计为了解决并发的问题,我们计划做一个更加高效强壮的队里处理系统。 背景 由于PHP不支持多线程,但是作为一个完善的系统,有很多操作都是需要异步完成的。为了完成这些异步操作,我们做了一个基于Redis队列任务系统。 大家知道,一个消息队列...

    booster 评论0 收藏0
  • php7+的php-fpm参数配置,注意事项

    摘要:安装的,如果的这几个参数设置不当了,会导致启动不了,站点不能解析文件,报错误。 安装php7+的,如果php-fpm的这几个参数设置不当了,会导致php-fpm启动不了,nginx站点不能解析php文件,报404错误。 相关命令:centos7+,启动php-fpm: systemctl start php-fpm 查看php-fpm是否启动: ps -ef|grep php ph...

    zhangrxiang 评论0 收藏0
  • JavaScript高程三----(基础一)

    摘要:大小写的不同分别表示不同的变量。本质由一组无序的名值对组成的。字符串中第一个小数点有效,第二个无效,后面的字符串会被忽略。注意双引号开头,必须以双引号结尾,单引号也是如此转义字符表示非打印字符或具有其他用途的字符。 JavaScript高级程序设计(第3版)读书笔记 1.区分大小写: 变量、函数名和操作符都要区分大小写。大小写的不同分别表示不同的变量。 2.标识符: 变量、函数、属性...

    cppowboy 评论0 收藏0

发表评论

0条评论

zhangyucha0

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<