你是不是一直都默認 Math.round 是四捨五入呢,我一直以來也是,直到最近我處理到toFixed 的問題時好奇跑過來嘗試 Math.round,發現事情不是我想的那樣,至於toFixed 有啥問題那又是另一回事了。

先來看看維基百科的描述

四捨五入 若所取位數的位次後一位小於等於4,則捨去;反之,若大於等於5,則進位。若原數值為負數,則先以絕對值求得結果後再加負號。

我們再來嘗試 Math.round,你會發現有一個的結果跟用維基百科做完的運算其實不同

Math.round(1.4) // 1
Math.round(1.5) // 2
Math.round(1.6) // 2
 
Math.round(-1.4) // -1
Math.round(-1.5) // -1 ?
Math.round(-1.6) // -2

後來去 MDN 看也有段在描述這件事,但實際上只是提醒並沒有跟妳說為什麼,那想當然最後的手段就是去 ecmascript 找規格描述。

撇除前面的數值檢查只看最後一個步驟「Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.」

想辦法找到一個最接近 n 的整數,如果有兩個整數與 n 距離相同也最接近,選接近正無限的那個整數。

如何快速找到這個整數呢,其實很單純,先將 n 去掉小數點(這裡先取名為 int_n)如果是正數就是 int_n, int_n + 1,如果是負數就挑 int_n, int_n - 1,如果你想保險一點就在減一或加一。

再來就是用你挑好的整數與 n 相減取絕對值然後比誰最小,如果距離相同選最接近正無限的那個整數,這裡用 1.5 舉例

  1. 找到最接近 n 的整數,這裡就會是 1, 2
  2. 將挑好的整數與 n 相減取絕對值然後比誰最小
    1. |1 - 1.5| = 0.5
    2. |2 - 1.5| = 0.5
  3. 兩個結果相同就選最接近正無限的整數,所以這裡最後會選 2,答案就是 2

我們將這個公式套到那個有問題的數值上,也就是 -1.5

  1. 找到最接近 n 的整數,這裡就會是 -1, -2
  2. 將挑好的整數與 n 相減取絕對值然後比誰最小
    1. |-1 - (-1.5)| = 0.5
    2. |-2 - (-1.5)| = 0.5
  3. 兩個結果相同就選最接近正無限的整數,所以這裡最後會選 -1,答案就是 -1

聰明的你肯定發現的這個公式跟維基百科寫的似乎不同,少了先取絕對值再做運算的動作,不過就算沒取絕對值,也應該在 n 為負數時與 n 等距的整數上挑最接近負無限的那個。

這是怎麼一回事呢,我不得而知,反正被我無意間發現了,如果沒特別去驗證,我大概永遠不會發現。