日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > php >内容正文

php

PHP7 serialize_precision 配置不当导致 json_encode() 浮点小数溢出错误

發布時間:2024/9/20 php 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PHP7 serialize_precision 配置不当导致 json_encode() 浮点小数溢出错误 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

事情是這樣的,項目里發現一個奇怪的現象,json_encode一個帶浮點價格的數據, 出現溢出, 比如:

  • <?php

  • echo json_encode(277.2);

  • // 輸出結果為: 277.199999999999989

  • 這明顯是不能接受的, 數據雖然很接近, 但畢竟已經變更了
    下意識地認為這是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] = '\0';

  • }

  • smart_str_appendl(buf, num, len);

  • }

  • 是的, 竟然發現了配置項serialize_precision, 這個配置我當初的理解是serialize()方法用的, 而我當初嘗試修改過配置項precision, 然并卵, 原來json_encode會用到serialize_precision, 于是修改php.ini, 把它設為16, 原來是17還是18忘了.

  • <?php

  • echo json_encode(277.2);

  • // 輸出結果為: 277.2

  • 如果你想重現我的問題, 很簡單, 把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, 來看測試結果:

  • <?php

  • echo json_encode(277.0);

  • // 277

  • echo json_encode(277.0, JSON_PRESERVE_ZERO_FRACTION);

  • // 277.0

  • 來源:https://blog.csdn.net/moliyiran/article/details/81179825

    總結

    以上是生活随笔為你收集整理的PHP7 serialize_precision 配置不当导致 json_encode() 浮点小数溢出错误的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。