雜記 › 技術筆記 › 貼上 100 行分數,第 45 行被悄悄算成 1/4:約分這頁的負號、空行和大數各歸誰管

貼上 100 行分數,第 45 行被悄悄算成 1/4:約分這頁的負號、空行和大數各歸誰管

100 行分數裡摻了 5 行錯的,頁面只報 4 行:帶分數被靜默算成另一個數,負號統一搬到分子,大數在解析那一步就換成浮點。

第 45 行那個帶分數,被算成了另一個數

把一批分數練習整列貼進約分頁的批次框,100 行進去、100 行出來,逐行核對才發現第 45 行不對。我寫的是 1 又 3/4,串是 1 3/4,頁面回的是「第45行1/4 = 1/4 (0.25)」——它沒報錯,只是把整道題換成了另一個數。原因在解析那一步:批次走第 167 行,先按半形斜線切成兩段,再把兩頭交給 parseInt,而這個函數讀到空格就停。於是 2 1/2 得 1,1+3/4 同得 1/4,1.5/2 得 1/2。真正會報錯的是另一類:3/4/5、0.75、還有用全形斜線寫的 1/4,都判「格式錯誤」。

區分這兩類很要緊。報錯的行你一眼就看得見,靜默改值的行穿著正常結果的外衣混在批次的紅字之外——100 行裡摻的 5 行只有 4 行被判錯(「格式錯誤」3 次加「分母為0」1 次),剩下那行正是改了值的帶分數。

負號只有一個家,分母那邊永遠不留

約分頁的範本在第 81 行做了一次翻號:負號只往分子上搬,搬完分母側恆為正。所以 -3/4 和 3/-4 都輸出 -3/4,-3/-4 輸出 3/4。批次那側還有多餘的動作,第 178 行把結果重拼成「原文 → 最簡」的形態,於是原文裡空格與負號會被照抄回去再補上落點,寫成 3 / -4 的行回顯成 3/-4 → -3/4。

負零也不用擔心。輸入 -0/5 與 0/-5,最簡、等值小數、百分比、約分倍數這 4 個輸出依序給 0、0、0.00%、1/5,頁面上任何一條路徑都不會留下字面的負零,因為 String 把負零寫成 0。

分母為 0 根本輪不到求公約數

這一頁把除零攔在上游:單條在第 123 行判掉、第 125 行給出「分母不能為 0」這句提示,批次在第 173 行給「分母為0」。同一條路徑、同一句文案涵蓋了 0/0、5/0、5/-0、5/0.0 這幾種寫法,而第 80 行與第 86 行裡那兩處針對分母為零的分支,在介面上其實走不到。還有一種更繞的命中:0b11/0b100 也被判分母為 0,因為 parseInt 不認二進位前綴,兩頭都讀成 0。

批次的行是 1:1 的,行號卻不是

100 行進去是 100 筆結果,textarea 裡那 598 字元一條不丟,失敗的行也按原位插成紅字,整批不中斷——第 166-183 行的迴圈裡那 3 處提前返回只跳過當前行。輸出片段量過是 22167 字元、22727 位元組。可第 165 行那個 validCount 被讀取 0 次,頁面上沒有任何行數或成功數的彙總,你得自己數。

行號還是重排過的:8 個實體行裡含 4 個空行,只渲染 4 筆,實體第 8 行那道 24/36 被標成「第4行」。換行方面 CRLF 由第 156 行的 trim 兜住,3 筆正常;裸回車把 3/6 與 4/8 連起來時,整串被當 1 行報格式錯誤。

寫法這一頁怎麼處理批次裡的回顯
1 3/4空格處截斷,靜默改值第45行1/4 = 1/4 (0.25)
3/4/5切出三段,判非法格式錯誤
5/-0上游攔截,不進求公約數分母為0
9007199254740993/6先摺成 double 再約4503599627370496/3

單條那兩個框是 number 型別

貼 +3、1/1、0x10 甚至 ½ 進分子框,瀏覽器的淨化發生在 parseInt 之前,整框被清成空字串,第 121 行的 isNaN 直接 return,兩張卡都不顯示、錯誤欄是空字串——連一句報錯都沒有。逐字敲進去更坑:3/4 被吞成 34、0x10 被吞成 010 之後照樣算,得數欄一路算到 5/2。

等值小數沒有循環節,只有一條 14 字元門檻

第 89 行的判據是把除法結果轉成字串後看長度是否大於 14,命中就用 toFixed(10) 再補三個點。於是 1/3 顯示成 0.3333333333...、1/6 顯示成 0.1666666667...(末位是進位來的)、1/999 顯示成 0.0010010010...。反過來,能除盡的 1/8192 因為結果有 15 字元也被砍成 10 位,而 1/4096 正好 14 字元,原樣留著。分數之間要接著算,分數運算那類入口給的是通分後的精確結果;只想看小數位數,交給小數格式化按位裁更直接。

大數在 parseInt 那一步就換成了浮點

範本裡 BigInt 出現 0 次,第 113/114 行和第 167 行的 parseInt 把長串摺成 double,超出安全整數就丟尾數。實測 9007199254740993/6 這一題,頁面給 4503599627370496/3,按整數真值算是 3002399751580331/2,差 -0.166666…;999999999999999999/3 更彆扭,頁面判定它已是最簡並回顯 1000000000000000000/3,真值是 333333333333333333,差 +0.333333…。還有兩種寫法會走偏:1e21/3 的 parseInt 在第 1 個字元 e 處停讀,整題變成 1/3;相鄰的兩個大整數 9007199254740993/9007199254740992 被摺成同一個數,直接輸出整數 1。

輸入串9007199254740993/6頁面輸出4503599627370496/3整數真值3002399751580331/2兩者相差-0.166666…

圖 1:同一道題在 parseInt 前後各是什麼,尾數在哪一步改掉,差額就出現在哪一行。

位數再往上就形態都保不住:12345678901234567891/2 得 6172839450617284000,與真值差 -361.5;200 位分子的串先被摺成 1.2345678901234567e+199,最簡欄直接輸出 6.172839450617284e+198/3,百分比欄給 2.0576131502057615e+200%,分數在這一欄已經不成其為分了。

常見問題

Q:批次結果裡怎麼分辨哪行被靜默改了值?

看原文那一段。第 178 行會把輸入原樣回顯再補結果,所以 1 3/4 這類會顯示成「1/4 = 1/4」——等號左邊已經沒有你寫的那個數了。頁面不給彙總,只能逐行比。

Q:帶分數到底能不能寫?

這一頁只認兩段式:分子、分母各一段,第 75-78 行的輸出也只有整數與分數兩種形態,7/1 會顯示成 7。要輸入 1 又 3/4,先自己換算成 7/4 再貼。

Q:超過 16 位的分子分母該怎麼約?

別指望這一頁,它在解析階段就轉成浮點了,實測連判「已最簡」都會看錯。要嘛先把數拆小,要嘛用支援任意精度的整數運算環境。

📎 相關文章

把 3. 起步貼進 Markdown 轉 HTML,為什麼有序列表的編號只認第一個數 2026-11-03 文字切成 43 片再拼回去少了 42 個位元組,切口其實都落在換行上 2026-11-03 Crontab 表達式 0 0 13 * 5 到底哪天跑:這一頁只排 2 次,真實 cron 是 62 次 2026-11-03 16 組開關實測 HTML 去標籤:報 19 個標籤,實際刪掉 148 個字元 2026-10-30 SVG 轉 PNG 後外部引用的圖丟了一半:同一份 594 B 檔案預覽有 3 張圖、成品只剩 1 張 2026-10-30