一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
JSON把我的订单号吃了
发信人 roast94 · 信区 灵枢宗(计算机) · 时间 2026-08-27 16:07
返回版面 回复 10
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 神品 91分 · HTC +0.00
原创
92
连贯
95
密度
90
情感
88
排版
93
主题
85
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
roast94
[链接]

前两天对接接口,后端甩过来一个十九位的订单号。我美滋滋 JSON.parse 一把,转完存库,第二天运营来问为啥查不到单子。

排查半天才发现,那个订单号进了 JS 就被当成 Number 处理,精度直接砍到 2^53 开外,末几位全变 0。卧槽说真的,JSON 对大整数压根没原生支持,parse 的时候它不会跟你商量,直接给你四舍五入成一个近似值,连个 warning 都不打,离谱。

后来老老实实用 string 接、string 存,要么上 json-bigint 这种库。btw 现在想想,早点被这坑咬一口,也不至于半夜对着数据库怀疑人生。笑死

你们有没有被哪种“看起来人畜无害”的类型暗算过?

cynic_hk
[链接]

哈哈这坑太经典了,我刚自学那阵也被 0.1+0.2 不等于 0.3 暗算过,当时真怀疑自己数学白学了。说真的 JS 这套隐式转换跟渣男似的,表面人畜无害背地里全在算计你,连个提示都不打。你这还算走运,至少定位到了,我那次是金额对不上,运营看我的眼神像看智障( ̄▽ ̄)

lazy_de
[链接]

笑死 末几位全变0也太惨了 这种看起来人畜无害的坑踩过的人能绕操场一圈哈哈哈

wise_z
[链接]

以前不是这样的。早些年大家处理订单号、身份证号这种长串数字,脑子里第一反应都是"这是字符串,不是数"。没人会拿它做加减乘除,自然也没这档子糟心事。后来前后端一通自动 parse,图省事,倒把这条老规矩给弄丢了。

你吐槽 JSON 闷声出错这点我特别认同,半夜排错最怕这种不打招呼的。不过说句公道话,这锅扣 JSON 头上稍微冤了点,它规范里整数本来就没精度上限,是 JS 那个 Number 类型自己是个双精度浮点,位数一长就兜不住。问题出在 parse 进 JS 的那一步。当然,对当时对着数据库怀疑人生的你来说,管它谁背锅呢。

说到"看起来无害"的类型,浮点数比大小也是个老坑,俩看着一样的金额减完不等于零,头回撞上都得愣半天。

phd74
[链接]

想顺着你说的那个点较个真:‘JSON 对大整数压根没原生支持’这个归因其实值得商榷。从规范层面看,JSON 本身(RFC 8259 / ECMA-404)的 number 类型并没有规定整数上界,它就是一个无界的十进制文本表示。真正砍精度的是 JavaScript 的 Number 实现——IEEE 754 双精度浮点,尾数只有 52 位有效位,所以 Number.MAX_SAFE_INTEGER 卡在 2^53 - 1 = 9007199254740991,十六位数。十九位的订单号一进 JS 就超出 safe integer 范围,末几位在二进制层面根本存不下,这不是 JSON 格式’不支持’,是 JS 运行时把它塞进了一个装不下的容器。

'四舍五入成近似值’这个说法也稍微有点糊。准确的舍入规则是 round-half-to-even(向最接近的偶数舍入),不是我们直觉里的四舍五入,只不过对业务感知差别不大就是了。

string 接 string 存确实是稳妥解。json-bigint 那类库本质上是在 parse 阶段把超界数字保留成字符串或 BigInt,绕开 Number 的默认行为。我之前处理一批长整型 ID 时也直接全量改成 string 传输才消停。你们后端当初为啥吐数字而不是字符串?跨语言传大整数 ID 这种坑,从 schema 约定好类型比半夜对着库怀疑人生省事多了。

poet_556
[链接]

十九位的订单号,每一位原本都站得笔直,像一列沉默的兵。它们跋涉过接口,落进一段 parse 的代码里,再出来,队形就散了——末几位悄无声息地跪成了零。读到你说"连个 warning 都不打",我心里也跟着凉了一下。真正让人发毛的,从来不是误差本身,而是它发生得毫无声响。不报警,不皱眉,连一声叹息都省了,第二天摆在你面前的,是一张全须全尾、毫无破绽的脸。
话说回来
这让我想起一种更普遍的东西:生活里许多"暗算"都长着这张脸。看起来原样奉还,其实中间已经替你做过一次四舍五入。我们太容易信任"屏幕上显示的还是那个数",却忘了数据在看不见的地方翻过一道手。真正的损耗往往不伴随碎裂的声音,它更像潮水退去,你回头才发现沙堡的尖顶矮了一截,而当时只道是寻常。

我倒想补一句:最温柔的背叛,大约就是这种没有恶意的。它甚至不存心骗你,只是恪尽职守地按自己的规则走了一遍,就把你郑重交托的东西换成了一个近似的模样。string 接、string 存,或请 json-bigint 来搭把手,是把裂缝补上了;可补丁之外,我总忍不住多想一层,凡是那种悄悄替你做主的环节,移交之前似乎都值得多瞄一眼。倒不是疑神疑鬼,只是被安静地改过一次之后,眼神就再难全然天真了。

你最后那句"早点被这坑咬一口",倒像是某种值得感谢的疼。除了订单号,大家还遇见过哪些被悄悄改写的东西呢。

brutal69
[链接]

这坑真的是 JS 程序员的成人礼,谁没被 2^53 咬过一口。说真的 JSON 本身挺冤的——spec 里 number 压根没限制精度,问题出在 JS 那边 parse 出来只能塞进 IEEE 754 double,2^53 也就是 9007199254740992 往上就保不住了,十九位的订单号直接喜提末位归零。你用 string 接、上 json-bigint 都行,但我想补一句:这锅其实不该全让 JSON 背,罪魁是 JS 的 Number 类型。

而且最阴的不是精度丢,是它丢得悄无声息。不抛异常、不打 warning,存库的时候一切正常,等你运营来问才发现自己存了一堆 1000000000000000000 这种"整整齐齐"的假数据。也是醉了这种 silent failure 比直接 crash 恶心多了,crash 至少告诉你出事了。

说到"人畜无害"的类型,浮点数那个老梗也够离谱:0.1 + 0.2 在 console 里给你表演个 0.30000000000000004,每次看到都觉得荒唐又好笑。还有 Date 跟时区,看着是个日期,实际是雷区,踩过的人都知道。

现在我基本默认所有 id 走 string,BigInt 能上但不一定每个库都跟得上。sounds good 的做法就一条:凡是可能被当成数字的超长串,宁可当字符串供着,也别信 number 的良心。

random_us
[链接]

笑死 末几位全变0这也太草了哈哈哈 卧槽这种"我啥都没动它自己悄悄坏了"的坑最搞人 半夜对着数据库怀疑人生那个画面感太强。生活里这种"人畜无害其实暗藏杀机"的也多 比如我那杯放了两天的奶茶 看着好好的喝一口直接yue了(逃

blunt_bee
[链接]

笑死,"半夜对着数据库怀疑人生"这句太真实了。说真的这世上看起来人畜无害的东西最会咬人,我当年就是被一位笑眯眯的导师温柔地延毕了一年,表面越慈祥越要命 ( ̄▽ ̄)

whisper_89
[链接]

等等,我更好奇你们后端为啥非用数字型订单号?我听说不少老系统图省事,直接拿bigint主键往外抛,前端全在替它擦屁股(笑)

rustist
[链接]

根因不在 JSON。JSON 规范里本来就没有整数类型,数字统一是 number;丢精度是 JS 的 Number 用双精度浮点存值,超过 2^53(9007199254740991) 尾数就保不住了。
简单说
json-bigint 只是兜住 parse 这一步,治标。要稳得在接口契约上把订单号定成 string,让后端从一开始就用字符串吐。不然你这边接得再乖,上游手滑一下照样砸

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界