杂记 › 技术笔记 › 项数填 1e3 只算 1 项,明细最多 500 行:等差数列计算器的三套项数口径

项数填 1e3 只算 1 项,明细最多 500 行:等差数列计算器的三套项数口径

和是 2、列表只有 1 行,屏幕零提示

坑从一串字符开始。项数那格填科学计数法 1e3,想的是 1000 项,页面给出的和却是 2、明细列表只有 1 行,实测 validity 全 false 而屏幕零提示。填 10.9 更迷惑:页面按 10 项算,和仍是 155、列表 10 行,因为这一格是向零截断而非四舍五入;填 .5 得到 NaN,直接落空态;填 -10.9 截成 -10,同样空态。而首项与公差两格用 parseFloat 全程不取整,「支持小数」那句文案只对这两格成立,项数不在其中。

同一个 n 进了三个地方,得到三套口径

要摊开说的是:这一个 n 进了求和、明细、复制三处,覆盖范围互不相同,且没有一处会告诉你另一处的范围。

口径覆盖规则n=501 实测n=10000 实测(a1=1、d=3)
求和 S_n闭式覆盖 n 项按 501 项计149,995,000,覆盖 10000 项
明细列表硬截在前 500 项,再补一行提示501 个 DOM 行501 行
复制产物恒等于前 500 项500 个数500 个数、2629 字节
n=501n=1000050150150010000501500求和明细复制求和明细复制

图 1 两组同比例画三处覆盖的项数:左边只差一行提示,右边差的 9500 项全在屏幕之外。

明细为什么恒停在 501 行

正向第 145 行与反向第 194 行是同一句 Math.min(n, 500)。n=500 出 500 行且毫无提示;n=501 出 501 个 DOM 行,500 项数据后面多的是「... 共 501 项,仅显示前 500 项 ...」一行;n=10000 与 n=999999 都停在 501 行,变的只是提示里的总数。于是行数永远不等于项数:比复制条目多 1(多的就是那行提示),比真实项数少 n-500。文案里「生成前 n 项列表」这句正是栽在这里。

这条截断也不是渲染瓶颈:n=10 整页重绘 0.7 ms,n=500 是 9.8 ms,n=10000 仍是 12.5 ms,反向 n=100000000 是 16.1 ms,连续 3 次触发 n=10000 共 33.8 ms——成本被 500 行钉死,项数再大也不涨。模板里 setTimeout、debounce、requestAnimationFrame 各 0 次,没有节流。

toast 只弹不清屏,屏上留的是上一轮

正向有一条 n>10000 的守卫,写在第 131-134 行,命中就直接 return。它只弹一条「项数过多,请控制在 10000 以内」的 toast,不清空已有结果。实测 n=10001、15000、999999、100000000 四次都只弹 toast,屏上仍是上一次 n=10000 的 150,005,000 与 501 行。更糟的是下一步:此时点「复制数列」,拿到的还是旧的 500 项、2629 字节,剪贴板和 toast 互相打脸。

反向求解根本没有上限分支

同一条 n 换到反向求解,守卫就不存在了。同一个 1e3 在这里 parseInt 得到 1,撞上第 181 行的 n<2,于是空态;而 n=999999 与 n=100000000 都照常出结果,分别是 S=15,499,984.5 和 S=1,550,000,000,明细同样只有 501 行。正向会拦(虽然拦了不清屏),反向完全不拦,两边对大项数的处理并不对称。再加上截断同样发生在字母 e 处:1e7 按 1 项算,10e1 按 10 项算,而 parseFloat 能把同一串读成 1000、10000000、100——项数这格的输入语义和另外两格不是一套。

三方差额是一笔能对出来的账

a1=1、d=3、n=10000 这组里,页面 S_n=149,995,000 覆盖 10000 项;屏幕上可见的 500 项之和只有 624,750,缺 9500 项,覆盖率 0.25%;复制串 2629 字节、500 个数、0 个换行、最后一个数是 1498。习惯把屏上的数加一遍去核对 S_n 的人永远对不上,但成因不是精度:求和走第 139 行的闭式 n*(a1+an)/2,第 146-148 行逐项独立生成、无累加,页面从不把明细相加,两边本就不同源。

浮点在这一篇只占一行:闭式与逐项累加在 15 组样例里有 3 组分家,最早落在小数第 4 位(a1=5、d=0.1234、n=17),最晚在小数第 15 位,而 fmt() 的 maximumFractionDigits: 10(第 115 行)把它们全显示成同一个数。

真正会翻车的是溢出那一格

第 139 行先加后除,把溢出临界定在 MAX_VALUE/2 = 8.988465674311579e307。d=0、n=1 时,a1=9e307 的和直接显示 ∞,而数学上 S₁ 恰好等于 a₁,末项那一格还是有限长串;a1=1e308、d=1e308、n=10 更彻底,明细第 1 行是一条 411 字符的千分位串(309 位数字加 102 个逗号),第 2 到 10 行与末项、和、平均值全是 ∞,页面既不报错也不隐藏结果。

把这三口径分开用

  • 项数只填纯整数:等差数列计算器的项数格会在字母 e 处截断,1e3、1e7 都按 1 项,10.9 静默截成 10,.5 与 -10.9 落空态。
  • 要 500 项以上的明细就拆段跑:从 n=501 起,可复制的只有前 500 项,多出的第 501 行是提示而不是数据。
  • 见到「项数过多,请控制在 10000 以内」先把结果区当空白再点复制,屏上的 150,005,000 与 501 行是上一轮残留。
  • 核对求和不要拿屏上的数相加:S_n 覆盖 n 项,可见区只有 624,750 那个量级。
  • 统计条正向只有 4 项(首项、末项、项数、平均值,不含求和),反向 4 项(有公差、无平均值),文案宣称的 5 项在实测里不成立。
  • 文案 12 条断言核过是 6 条一致、4 条冲突、2 条部分一致,引用「前 n 项列表」前先按 min(n,500) 折一下;等比数列页另有其口径,见那篇实测。

常见问答

Q:项数填 1e3 为什么只算 1 项?

这一格用 parseInt(第 123 行),读到字母 e 就停,所以 1e3 只剩 1;首项和公差用 parseFloat,同一串能读成 1000。想要 1000 项就直写 1000。

Q:明细多出的一行是第 501 项数据吗?

不是。第 145 行与第 194 行都把渲染范围截在前 500 项,n=501 之后的那行只是「仅显示前 500 项」的提示,n=10000 与 n=999999 都停在 501 行,复制条目恒为 500。

Q:提示「项数过多」之后屏上的和还能引用吗?

不能当作本次结果。第 131-134 行的守卫直接 return,只弹 toast 不清屏,屏上是上一次 n=10000 的 150,005,000 与 501 行,此时点复制拿到的也是旧的 500 项、2629 字节。

📎 相关文章

文本对比的代价全在 (m+1)×(n+1) 这张表:6000 行还能跑,8000 行直接堆内存崩 2026-10-28 10 万个键全进 DOM:JSON 视图的渲染规模、爆栈深度与复制重排 2026-10-28 带 g 的正则是有状态的:同一个 RegExp 连打 4 次 test 只过 2 个 2026-10-28 JPG 转 PNG:27760000 个像素一位没变、体积涨到 15.881 倍,JPEG 的块效应被原样锁进无损容器 2026-10-28 20 行文件名拆成 18 行两列:粘进 Excel 只占 1 列,1e5 变成 100000 2026-10-28