1.前言
最近在工作中,后臺(tái)出現(xiàn)了這樣的錯(cuò)誤,傳遞的手續(xù)費(fèi)是29塊錢,但是傳遞到了第三方接口就報(bào)錯(cuò)了,查看日志顯示該手續(xù)費(fèi)變成了28.999999999999996,日志如下:
明明是29怎么就變了?通過斷點(diǎn)檢測(cè),原來是json_encode()轉(zhuǎn)換的問題
2.案例演示
$param={
‘a(chǎn)mount’=>5000,
‘fee’=>29
}
var_dump(json_encode($param));//{"amount":5000,"fee":28.999999999999999999996}
3.解決方法
3.1方法1:強(qiáng)行轉(zhuǎn)換成字符串保證精度
$request['param']['feeAmount']=(string)$request['param']['feeAmount'];
注意:使用這種方法千萬要注意,對(duì)接接口是否有變量類型要求
3.2方法2:格式化數(shù)字number_format函數(shù)
number_format(number,decimals,decimalpoint,separator)
參數(shù):
number參數(shù)是要格式化的數(shù)據(jù)
decimals參數(shù)是保留的小數(shù)
decimalpoint參數(shù)是規(guī)定用作小數(shù)點(diǎn)的字符串
separator參數(shù)是規(guī)定用作千位分隔符的字符串
案例:
$request['param']['feeAmount']=(int)number_format($request['param']['feeAmount'],0);
注意:number_format返回的是字符串string,要注意接口是否有規(guī)范要求,如果有,則須強(qiáng)行轉(zhuǎn)換為int或接口規(guī)范的類型
疑問1:這時(shí)候應(yīng)該有人在想能不能直接強(qiáng)制轉(zhuǎn)換為int呢?注意,float強(qiáng)制轉(zhuǎn)換成int有坑!
答:int類型是向下取整的,比如:12910.9 會(huì)被轉(zhuǎn)換為 12910
疑問2:這時(shí)就會(huì)有人問我,浮點(diǎn)數(shù)顯示的是8,為什么轉(zhuǎn)換成整數(shù)會(huì)變成7?
答:floor((0.1+0.7)*10),其結(jié)果是7而不是8,是因?yàn)樵摻Y(jié)果內(nèi)部表示的是7.9999.....所以不要相信浮點(diǎn)數(shù)結(jié)果精確,也不要比較兩個(gè)浮點(diǎn)數(shù)是否相等
3.3方法3:修改配置項(xiàng)serialize_precision
json_encode() 轉(zhuǎn)換浮點(diǎn)小數(shù)溢出現(xiàn)象只出現(xiàn)在PHP 7.1+版本,是因?yàn)閜hp源碼對(duì)于json_encode()轉(zhuǎn)換使用到了serialize_precision配置項(xiàng),如下圖
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);
}
關(guān)于PHP函數(shù)serialize_precision (integer)的一些概念了解:
適用范圍:PHP_INI_ALL;
默認(rèn)值:100
serialize_precision指令的數(shù)量決定了雙打和彩車被序列化后的浮點(diǎn)數(shù)字存儲(chǔ)。設(shè)置到一個(gè)合適的值,確保精度的數(shù)字時(shí),可能丟失以后反序列化。所以我們要使得json_encode()轉(zhuǎn)換浮點(diǎn)數(shù)沒有小數(shù)溢出,建議使用默認(rèn)值 serialize_precision = -1 即可 。
3.4知識(shí)點(diǎn)補(bǔ)充
json_encode有個(gè)選項(xiàng)JSON_PRESERVE_ZERO_FRACTION,表示如果是個(gè)整數(shù), 是否保留小數(shù)點(diǎn)和尾數(shù)0,舉例如下:
<?php
echo json_encode(223.0);// 223
echo json_encode(223.0, JSON_PRESERVE_ZERO_FRACTION);// 223.0
3.5其他取整函數(shù)
四舍五入取整 round(param)
向上取整 ceil(param)
向下取整 floor(param)
4.bcsub()函數(shù)精度相減
格式:
string bcsub ( string $left_operand , string $right_operand [, int $scale = int ] )
說明:
2個(gè)任意精度數(shù)字的減法
參數(shù):
left_operand:字符串類型的左操作數(shù).
right_operand:字符串類型的右操作數(shù).
scale:此可選參數(shù)用于設(shè)置結(jié)果中小數(shù)點(diǎn)后的小數(shù)位數(shù)。也可通過使用 bcscale() 來設(shè)置全局默認(rèn)的小數(shù)位數(shù),用于所有函數(shù)。
返回值:
返回減法之后結(jié)果為字符串類型.
代碼案例:
<?php
$a = '1.234' ;
$b = '5' ;
echo bcsub ( $a , $b ); // -3
echo bcsub ( $a , $b , 4 ); // -3.7660
————————————————
版權(quán)聲明:本文為CSDN博主「拯救世界的派大星」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/m0_46266407/article/details/105556444
以上是由福州網(wǎng)站建設(shè)的小編為你分享了"踩坑之json_encode精度丟失問題"文章,如果你在這方面有什么問題,隨時(shí)聯(lián)系我們