杂记 › 技术笔记 › 3.7 读成 3、1e3 读成 1:分数计算器这一页先在输入端截成整数,再在 14 个字符处换显示口径

3.7 读成 3、1e3 读成 1:分数计算器这一页先在输入端截成整数,再在 14 个字符处换显示口径

分子框填3.7被读成3,填1e3读成1、填0x10读成16;等值小数那格只看字符串是否超14个字符,1/4096恰好14原样通过,1/8192变0.0001220703...,1e-7只有4字符照样上屏。

我在分子框里填 3.7,这一页拿去算的是 3

起因很普通:分子填 3.7、分母填 2,另一个分数保持 1/3,运算符停在加号。页面给出 11/6,小数格写 1.8333333333...,没一处提示我它把 3.7 换了。

把模板(11127 字节 / 231 行)的脚本块整段摘出来,回读的 7 个函数源码与模板原文逐字命中,再喂进纯 Node 的 vm 跑 calc()。只看这一页的代码,没开浏览器,重绘那一类账一律没测。

输入端:四个 number 框被不带 radix 的 parseInt 又读了一遍

四个框的类型都写着 number,代码却把它们的值再 parseInt 一遍,不带第二个参数。于是 3.7 在句点前停住得 3,3.99 同样得 3——是截断不是四舍五入,两格都出 11/6;填 -3.5 得 -3,结果 -7/6,小数格 -1.1666666667...。

剩下几格更容易看漏。填 1e3 读成 1,它在字母 e 那里就停了,出来的 5/6 与直接填 1 完全同形。填 0x10 读成 16,十六进制前缀被无 radix 的写法认了下来,界面给 25/3;带 radix 10 的那条路只会给 NaN。填 -0 读成 0,结果格剩 1/3。这几格都在分数计算器上,只改分子那一格、其余三格停在默认值就能复现。

再往空的一侧走就更安静:任一格填空串、abc、纯空格、Infinity,四个值在 isNaN 判定上直接 return,两张卡一起被隐藏,既不报错也不清空——实测前后步骤容器的 innerHTML 长度都是 338,旧表达式与步骤文本一个字节没掉,下一次合法输入踩着旧串覆写。错误出口那两条也不擦:分母填 0 写「分母不能为 0,请检查输入」,连前面的警告符合计 21 字节;除法时分数 B 的分子填 0 写另一句,恢复合法输入后旧串仍在,只是父容器被隐藏。

分子框填parseInt 读到分数格等值小数那格
3.7311/61.8333333333...
3.99311/6同上
-3.5-3-7/6-1.1666666667...
1e315/60.8333333333...
0x101625/38.3333333333...
-001/30.3333333333...
分子框 → 无 radix parseInt → 读到几 你填 读到 停在哪 上屏 3.7 3 句点前 11/6 1e3 1 字母 e 前 5/6 0x10 16 前缀被认下 25/3 -3.5 -3 负号照收 -7/6 空串 NaN 直接 return 旧串 338

图 1 只画读取那一步:在哪个字符前停住,就送哪个整数进算式。

显示端:这把尺是 14 个字符,不是小数位数

等值小数那一格判的是 String(val) 的字符数是否超 14:超线走 toFixed(10) 再拼三个 ASCII 句点,不超线原样打印。于是 1/2048 那 13 个字符原样上屏,1/4096 正好 14 个字符也原样上屏,1/8192 是 15 个字符就被砍成 0.0001220703...,一个本来整除、末尾还带 3 位有效数字的有限小数只因总长度超线换了口径。整数结果也归这把尺管。

不超线那一支的原样是字面意义的:1/10000000 那一格实测就是 1e-7,只有 4 个字符;1/1000000 是 0.000001。这条线只量十进制写法,4 个字符的指数串根本走不到判定之前。

超线那一支的第 11 位参与四舍五入:1/7 真值 0.142857142857... 的第 11 位是 5,本页显示 0.1428571429,末位比真值大 1;1/11 的第 11 位是 0,所以显示 0.0909090909。代码注释那句「有限小数最多显示12位」既不按 12 位、也不判是否循环。

等值小数格:字符数与 14 这条线 分数 字符数 分支 屏幕上 1/2048 13 原样 0.00048828125 1/4096 正好 14 原样 0.000244140625 1/8192 15 超线 0.0001220703... 1/10000000 4 指数形态 1e-7 整数那格 16 超线 4503599627370496.0000000000...

图 2 那几行最直白:14 字符压线通过,15 字符被砍,4 字符的指数串没碰上这条线。

负分母那一步,同一行里有两把口径

公分母按绝对值算,通分因子却拿有符号的分母去除。于是 1/-2 加 1/3 的步骤行打成「公分母 = LCM(-2, 3) = 6」,紧接一行「分子 ×-3」,求和行「-3/6 + 2/6 = -1/6」:结果格符号是对的,同一个 -2 却一边被当参数显示、一边被绝对值吃掉。换成 1/-2 加 1/-3,两个因子都是负的,求和 -5/6。减法分支的通分行还不带「分子 ×k」那半句,与加法分支不同形。

符号的搬运只在 reduceFrac 那一步做,负号一翻面,约分行会凭空出现:-1/2 乘 -1/-3 的第一步打 1/-6,第二步打「GCD(1, -6) = 1」再打 -1/6,那个公约数等于 1,行却照样在。约分行的第一个参数也是有符号值,-1/2 加 -1/2 那行写「第三步:约分 GCD(-2, 2) = 2」,负号只出现在标签里。同一族另一页也有符号归属问题,那本账在单独做约分的那一页。

约分那一行是条件行,步数在 6 与 8 之间跳

1/2 加 1/3 的步骤卡只有 6 行,没有约分行;1/6 加 1/3 变 8 行,多出那行写「GCD(3, 6) = 3」;0/3 加 0/5 打「GCD(0, 15) = 15」,结果格是 0。判据一句话说完:公分母恒正,那一行出现当且仅当分子和与它的公约数大于 1,等于 1 时整块不拼进去,步数从 8 掉回 6。乘法分支是 2 行或 4 行,除法分支是 3 行或 5 行,3 除以 1/3 得 9 就不出约分行。分母为 1 时分数格只印分子,所以 0/3 乘 0/5 出的是单个 0,不是 0/1。

这把尺之外我只测到一处精度:99999999 乘 99999999 在这一页得 9999999800000000,BigInt 真值 9999999800000001,差 1。默认首屏就是 1/2 加 1/3 出 5/6,连调两次四个字段逐字节相同(表达式 11 字节、分数 3 字节、小数 15 字节、步骤原文 157 字节)。通分与约分该怎么教不在这篇,那是站内那篇算理文的范围。

Q:填 3.7 为什么一点提示都没有?

截断发生在读取阶段而不是校验阶段:parseInt 在第一个读不下去的字符前停住,剩下的 3 是个合法分子,页面没有理由报错。

Q:填 1e3 是想让它等于一千,为什么不是?

读取那一步在字母 e 处停住,读到 1,输出 5/6,与直接填 1 一模一样。

Q:约分那一行为时有时无,是漏渲染吗?

不是,它本来就是条件行;步数在 6 与 8 之间跳,说明判定走完了,只是那一块没被拼进去。

📎 相关文章

后缀只切最后一个点:archive.tar.gz 分走 gz,photo. 和 .gitignore 挤进同一格,表格首列还会掉一个点 2026-11-14 正则测试页永远带 g:填 gg 直接报错、填大写静默失效,21 处带组匹配只列 20 行 2026-11-14 星号段长写死:脱敏页五套掩码固定切出 4、3、11、12 枚星号,18 位串被手机档抢走、空框回落 * 2026-11-13 锁是单向的、百分比是两把尺:pct 50 得 619×430,PNG 四档 quality 全 6971 B 2026-11-13 相对时间的「一个月」写死 30 天:时间戳页六档除数、13 位毫秒输入与 UTC+8/UTC-5 两档截断实测 2026-11-13