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精度丟失問題"文章,如果你在這方面有什么問題,隨時聯系我們