和是 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 位元組 |
圖 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 位元組。