最近在编写接口的时候发现了一个很有意思的事情,大致是这样的一个场景:
前端定位后将经纬度传给后端,前后端使用json格式进行传输,后端在接收到json数据后,使用json_decode方法将json字符串转成数组,然后再获取其中需要的数据。此时问题就出现了,前端传参如下:
{"latitude":36.30556423523153,"longitude":104.48060937499996}
后端转化后
array(2) {
["latitude"]=>
float(36.305564235232)
["longitude"]=>
float(104.480609375)
}
然后发现精度丢失了,这怎么行,前端好不容易拿到这么精准的数据怎么可以截取呢?咱说干就干,DeepSeek走起。大致给出了如下方案
一、将小数转为字符串传递
在 JSON 中直接将小数以字符串形式传递,避免 PHP 自动解析为浮点数:
{"latitude":"36.30556423523153","longitude":"104.48060937499996"}
二、调整 PHP 精度设置
修改 php.ini
中的 serialize_precision
参数,控制浮点数的序列化精度:
serialize_precision = -1 ; 使用最高精度
三、使用 JSON_BIGINT_AS_STRING
选项
在 json_decode()
中启用 JSON_BIGINT_AS_STRING
,强制将大整数和小数解析为字符串(适用于整数部分较大的数值):
$data = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
四、高精度计算扩展
使用 BCMath
或 GMP
扩展处理小数运算,避免浮点误差:
$value = '36.30556423523153';
$result = bcadd($value, '0', 15); // 保留15位小数
五、解码后格式化输出
若精度丢失已发生,可用 sprintf()
或 number_format()
重新格式化:
$data = json_decode($json, true);
$formatted = sprintf('%.15f', $data['value']); // 输出:36.30556423523153
限制:若原始值已丢失精度,此方法无法恢复
实测结果
方案一(有效)
方案二(无效)
去检查了php.ini的配置,serialize_precision已经是-1了,但还是丢失了精度
方案三(无效)
加了第3、4个参数仍旧丢失精度
方案四(有效)
方案五(有效)
需要先获取小数位数,如果位数不一致又会导致得到的数据跟传参不一致
后续操作
拿到的数据是字符串类型,怎么转化为数字类型呢
$str = "36.30556423523153";
$cleanStr = preg_replace('/[^0-9.]/', '', $str); // 移除非数字和小数点
if (is_numeric($cleanStr)) {
$result = bcadd($cleanStr, '0', strlen(explode('.', $cleanStr)[1])); // 自动计算小数位数
echo $result; // 输出:30.329137666892592
}
