摘要:感謝地獄星星原因已找到該現(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] = "