JSON 在线解析 打开工具

JSON 超长数字为什么会丢精度:2^53 与雪花 ID

JSON 规范里数字没有精度上限,但绝大多数解析器把它实现成 IEEE 754 双精度浮点,能精确表示的整数上限是 2^53−1(9007199254740991)。超过这个值,末尾几位就再也回不来了——不是四舍五入,是信息直接丢失。

丢在哪一步:解析,不是显示

很多人以为只是"显示时被截断了",其实丢精度发生在 JSON.parse 的那一刻:文本里的 12345678901234567890 被读成 double 后就已经变成 12345678901234567168,后面无论怎么格式化、怎么再序列化,都只能拿到这个新值。

这也是为什么「复制出来看一看」没用——你看的是已经被改过的数。要判断有没有丢,只能比较原始文本与输出文本的字符。

哪些字段最容易中招

行业里的通行做法是把这些字段定义成字符串,从源头避开问题。但如果接口已经返回了数字,你能做的只有换一个保留原始文本的解析器。

怎么验证输出没被改值

拿一个超过 2^53 的整数贴进去,格式化后再看输出:

  1. 输入 {"id": 12345678901234567890}。
  2. 点格式化。
  3. 逐位比较输出里的数字与输入是否完全一致。

一致说明解析器保留了原始文本;如果末尾变成 ...567168 或 ...567000,就是走了 double。这个工具的结果是逐位一致。

自己的代码里怎么避免

JavaScript 侧有两个办法:一是用 JSON.parse 的 reviver 拿不到原始文本(已经晚了),需要改用能保留源文本的解析器;二是在服务端就把大整数序列化成字符串。

更彻底的做法是在接口契约里约定:所有可能超过 2^53 的整数一律用 string。这样前端、移动端、任何语言的客户端都不会踩同一个坑。

上面这些都不用装软件,也不用注册:打开页面就能用。

打开 JSON 在线解析 →

常见问题

2^53 到底是多大的数?

9007199254740992(2 的 53 次方)。双精度浮点能精确表示的整数上限是 2^53−1,即 9007199254740991,超过之后相邻两个整数可能对应同一个浮点值。

把数字加引号就安全了吗?

安全,但会改变类型。字段变成字符串后,接收方若按数字处理需要自己转换,且要小心转换时又用了 double。更好的做法是前后端在契约里统一约定这类字段的类型。

小数点后的精度也会丢吗?

会,而且更隐蔽。0.1 + 0.2 这类十进制小数在二进制里无法精确表示,涉及金额时应使用整数分或字符串,不要用浮点。