福州網站建設>網站新聞>php技術

        踩坑之json_encode精度丟失問題

        發布日期:2022-01-12瀏覽次數:1023 來源:福州網站建設

        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

        以上是由福州網站建設的小編為你分享了"踩坑之json_encode精度丟失問題"文章,如果你在這方面有什么問題,隨時聯系我們

        php技術有關的文章
        如果您有什么問題,歡迎咨詢我們客服! 點擊QQ咨詢