成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

PHP7 serialize_precision 配置不當(dāng)導(dǎo)致 json_encode() 浮點(diǎn)小數(shù)

zhangyucha0 / 2563人閱讀

摘要:感謝地獄星星原因已找到該現(xiàn)象只出現(xiàn)在版本上建議使用默認(rèn)值即可參考事情是這樣

感謝 @地獄星星:
原因已找到, 該現(xiàn)象只出現(xiàn)在PHP 7.1+版本上
建議使用默認(rèn)值 serialize_precision = -1 即可
參考: https://wiki.php.net/rfc/prec...
----------------------------------------------------------------------------------

事情是這樣的,項(xiàng)目里發(fā)現(xiàn)一個(gè)奇怪的現(xiàn)象,json_encode一個(gè)帶浮點(diǎn)價(jià)格的數(shù)據(jù), 出現(xiàn)溢出, 比如:


這明顯是不能接受的, 數(shù)據(jù)雖然很接近, 但畢竟已經(jīng)變更了
下意識(shí)地認(rèn)為這是php的一個(gè)bug, 不能準(zhǔn)確地json序列化一個(gè)浮點(diǎn)小數(shù)
這個(gè)問(wèn)題google了半天竟然也無(wú)果, 然后我跟同事說(shuō), 你們json_encode數(shù)據(jù)里有小數(shù)的時(shí)候, 記得先number_format()轉(zhuǎn)化成字符串.

自己又寫(xiě)了個(gè)fix方法, 遍歷數(shù)據(jù), 把is_float()的數(shù)據(jù)都用number_format()轉(zhuǎn)化了

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)){
        # 測(cè)試發(fā)現(xiàn)number_format有效數(shù)字低于18(保守取16)時(shí),不會(huì)溢出
        $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); // 當(dāng)18位有效數(shù)字處理(277.2已有4位有效數(shù)字)
// 277.199999999999989
echo number_format(277.2, 12); // 當(dāng)16位
// 277.2000000000000
echo json_encode(json_encode_pre(277.2));
// "277.2"

看到這結(jié)果時(shí), 便猜測(cè)是有效數(shù)字位數(shù)的問(wèn)題導(dǎo)致了溢出, PHP怎么會(huì)有這么蠢的設(shè)計(jì)呢?這是我當(dāng)時(shí)的想法.

然后想著是不是能從源碼下手, 于是查了下php源碼, 發(fā)現(xiàn)這段代碼:

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] = "