详解Javascript精度丢失以及解决方案

在前后端协作场景中,数字精度丢失 是极易被忽视却影响重大的问题。尤其当后端一旦数据包含较大的long类型值,就可能出现前端展示 ID 错误等问题。本文将从问题根源、解决方案到前端处理全流程拆解,给出可落地的实践方案。

底层原因

JavaScript 的数字类型本质

ECMAScript 规范中,JavaScript 的Number类型基于 IEEE 754 双精度浮点数(double)实现,所有常规数字(非 BigInt)均采用这种存储方式:

  • 总长度 64 位二进制:1 位符号位 + 11 位指数位 + 52 位尾数位(有效数字位)。
  • 尾数位的长度决定了Number无法精确表示所有整数,仅在特定范围内能保证整数的精确性。

Number.MAX_SAFE_INTEGER

JavaScript 内置两个常量界定 "安全整数范围":

复制代码
Number.MAX_SAFE_INTEGER; // 9007199254740991(2^53 - 1)
Number.MIN_SAFE_INTEGER; // -9007199254740991(-2^53 + 1)

超出该范围的整数,Number无法精准表示,会出现 "数字折叠" 现象:

复制代码
console.log(9007199254740991); // 9007199254740991(精确)
console.log(9007199254740993); // 9007199254740900(精度丢失)

风险场景

后端常用long类型表示以下核心数据:

  • 分布式 ID(雪花 ID)、用户 ID、角色 ID(64 位整数);
  • 订单号、交易流水号;
  • 毫秒级时间戳(System.currentTimeMillis())。

Java 的long是 64 位有符号整数,范围为[-9223372036854775808, 9223372036854775807](约 ±9.22e18),本身无精度问题,但传递到前端后:

复制代码
// 后端返回的JSON
{
  "id": 9223372036854775807
}

前端解析后会出现值篡改:

复制代码
const data = JSON.parse(responseText);
console.log(data.id); // 9223372036854776000(与原值不符)

这会引发订单查询失败、数据误操作、安全审计异常等严重问题。

问题根源

JSON类型特性

JSON 规范对数字的定义极为宽松:

  • 仅规定 "数字格式合法即可",不区分 int、long、double 等类型;
  • 无 "数值溢出" 概念,解析逻辑由具体编程语言自行处理。

例如以下 JSON 均符合规范,但解析结果因语言而异:

复制代码
{ "id": 123 }
{ "id": 9007199254740993 }
{ "amount": 3.1415926535897932384626 }

默认序列化行为

Java 后端常用 Jackson 将对象序列化为 JSON,默认配置下,Long/long类型会被序列化为 JSON 数字:

复制代码
public class User {
    private Long id;
    // getter/setter
}

序列化结果:

复制代码
{
  "id": 9223372036854775807
}

这种 "数字形式" 的传递方式,恰好触发了 JavaScript Number的精度短板。

核心根源总结 :后端将long以 JSON 数字形式传输 → 前端用Number接收超大数字 → 精度丢失。

实际解决方案

既然 "JSON 数字" 是问题导火索,核心解决思路是:后端将 long 类型序列化为 JSON 字符串,避免前端解析时的数字转换

在实际后端中,你可以通过设置全局Config文件来实现这个功能:

java 复制代码
@Component
public class GlobalConfig {

    /**
     * Jackson 全局配置:Long 类型转字符串,避免前端精度丢失
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder
                .serializerByType(Long.class, ToStringSerializer.instance)
                .serializerByType(Long.TYPE, ToStringSerializer.instance);
    }
}

你在前端同样需要将接受的ID类型从int改为string

JavaScript 精度丢失的本质是Number类型的存储限制,而前后端协作中,后端long类型的 JSON 数字传输是主要触发场景。通过 Jackson 将long序列化为字符串,前端保持字符串使用(或按需转 BigInt),可从根本上解决该问题。核心原则是:超大整数不传递数字字面量,改用字符串载体,这是保障数据精准性的关键。

相关推荐
guodingdingh13 分钟前
软件开发工作问题总结0718
java·开发语言·数据库
daols882 小时前
vxe-table 渲染器教程一:实现金额输入控件
javascript·vue.js·vxe-table
咖啡八杯2 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
触底反弹2 小时前
🔥 React 零基础入门(中):500 行屎山代码到组件化的蜕变
前端·javascript·react.js
优橙教育2 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵2 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
腻害兔4 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
碎光拾影4 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Miao121315 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔5 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程