php json_decode 精度丢失问题

最近在编写接口的时候发现了一个很有意思的事情,大致是这样的一个场景:

前端定位后将经纬度传给后端,前后端使用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
}
Tagged , ,