別被 0.1 + 0.2 給耍了

更新於 2024/07/24閱讀時間約 6 分鐘


「在 JavaScript 中 0.1 + 02 等於多少?」

這是我在面試時會問的一題。有經驗的工程師應該知道我在問什麼,但相信仍有不少人可能還不知道 0.1 + 0.2 不等於 0.3
幾年前有位同事跑來找我求救,他維護的某個早期專案出現線上問題,投資人在網頁上看到的金額與實際相差 1 塊。在聽完他的描述後,我問他這數字是前端計算的嗎?他回答是,此時直覺告訴我,這是浮點數精度問題。


浮點數精度問題?為什麼不等於 0.3?

簡單說,浮點數精度問題是指在使用 JavaScript 進行浮點數運算時可能出現的精度失真或精度丟失的情況。而造成浮點數精度問題的根本原因是電腦內部所使用的二進制浮點數表示法。
JavaScript 使用的是 IEEE 754 標準的雙精度浮點數表示法,即 64 位元,其中一位表示符號位,11 位表示指數,剩餘的 52 位表示尾數。
儘管這個表示法可以表示大範圍的數字,但它是基於二進制的,而我們通常使用的是十進制。這就導致了某些十進制小數無法完全精準地轉換為二進制表示,進而產生精度問題。例如,十進制中的 0.1 在該二進制中是一個無窮循環小數。這種問題在進行浮點數運算時尤為突出,因為即使是簡單的運算,結果也可能存在微小的誤差。

例如,在 JavaScript 中執行如下操作:

0.1 + 0.2 // 0.30000000000000004

根據我們的直覺,答案應該是 0.3。然而,在 JavaScript 中,由於浮點數精度問題,實際上結果可能是一個非常接近 0.3 的數字,但不是精確的 0.3

另一個造成精度問題的原因是浮點數的表示範圍限制。雖然雙精度浮點數可以表示非常大和非常小的數字,但在超出表示範圍時,將會出現溢出或下溢的情況,進而導致數字的失真。

在此就不對 IEEE 754 進行更詳細的說明,畢竟其浮點數行為與背後的原理已涉及計算機領域,相較之下前端工程師需更專注的是對浮點數的警惕與應對。如果有興趣了解原理,網上也有相當多資源。


凡遇浮點數,皆小心為上

在金融應用中,由於金額可能非常小且需要高精度計算,在進行複雜的計算時可能因此產生錯誤的判斷或造成四捨五入後的結果異常。
例如,計算某商品價格乘上某手續費後的最終價格,但因為浮點數精度問題,最終價格可能會有微小的偏差。以下列出幾個前端工程師可能遇到的情況。


1. 連續計算後的累積誤差:

在進行一連串浮點數的累積計算時,可能會累積誤差,導致最終結果與預期值不同。這在複雜的算法或迴圈迭代計算中尤其顯著。

let sum = 0.1
for (let i = 0; i < 10; i++) {
sum += 0.1
}

// 預期結果應為 1,但實際結果為 1.0999999999999999
console.log(sum)


2. 比較操作中的不確定性:​

在進行浮點數的比較操作時,可能會導致不符期望的比較或進入錯誤流程。

const foo = 0.1 + 0.2
const bar = 0.3

// 預期應該執行'小於等於'情境,但實際執行為'大於'情境
if (foo > bar) {
// 大於時執行某些動作...
console.log('大於');
} else {
// 小於等於時執行另外某些動作...
console.log('小於等於');
}


3. 不同計算順序的差異:

相同的數字在需求不變的情況下,即便只是計算順序不同,也可能因誤差而造成截然不同的結果。

const sum1 = 0.1 + 0.2 + 0.3 // 0.6000000000000001
const sum2 = 0.3 + 0.2 + 0.1 // 0.6
const isEqual = sum1 === sum2

console.log(isEqual) // 預期輸出 true,但實際為 false


不是你說不就不

既然前端計算浮點數是有風險的,相信一定會有人說,重要數字的計算本就不應該由前端處理,其實這算對也不算對。
金額、匯率、報酬率,手續費等等的數字在 domain 中的確是採後端計算結果為準並以此執行。但如果需即時呈現,假使都透過 API 取得,那就可能出現使用體驗上的不流暢,特別是當使用者就是變因之一時。

例如在基金交易中,手續費的玩法相當多樣,固定金額、單一費率、級距金額、級距費率、內含或外扣,適用基金標的或投資身份的優惠費率等等。
為了即時將結果呈現在畫面上,使用者於輸入框中每輸入一次金額就得透過 API 不斷重新查詢費率與計算手續費。
如果該數字不影響後續申購流程,那我們還可以讓這段邏輯以非同步方式執行。但如果後續流程中某些欄位受到該數字影響必須等待其結果呢?這時使用體驗上就會出現卡頓,即便你已經為輸入框做過防抖也一樣。
當你無法將使用者因素排除於變因之外,而使用者又能不間斷地改變因子時,這樣的體驗可能還沒等到上線,光是在 QA 就已經被提出了。

為此我們可能會這麼做,盡可能地在使用者未察覺的情況下透過 API 預先取得部分或全部計算邏輯。當需要計算時以落地方式立即處理,直接避開後端執行,API 響應時間與使用者網速等因素。當然這只是為了前台畫面的操作體驗與呈現,實際商業邏輯運算依然是後端處理。

工程師要懂得保護好自己

也因身處金融科技的產業特性,我會要求團隊內的工程師在做金額相關計算時一定要使用指定函式做特別處理。
千萬別小看那一點點的誤差,想像在電商購物被多扣一塊錢,你可能覺得沒什麼,大不了以後不用這家。但如果是定期定額交易時每次被多買一塊錢,你可能已經拿起手機撥打客訴專線了。不想被客訴甚至被主管機關盯上,那就別嫌麻煩,多做一步好好保護自己吧。




Cheng's murmur

來台北這麼多年,
最受不了的就是冬天那種要下不下的雨。
那傘撐也不是,不撐也不是。
avatar-img
2會員
5內容數
生活就是 早上 8 點的文湖線;晚上 8 點的 New York Sour;帶著一台 GR3X 意興闌珊的漫步;嚮往著午後草皮上陪拉布拉多 🐶 玩耍;拿起似有似無的筆開始敲打創作。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
京畿道烏山市的韓神大學近期因將烏茲別克留學生強制送出境引起爭議。 11月27日,韓神大學校方告知23位就讀 #語學堂 的烏茲別克學生,為領取 #外國人登錄證,須共同搭乘巴士前往出入境管理所。然而,該巴士卻在保全業者的護送下,開往仁川國際機場。教職員們這才告訴學生,這些學生的居留條件不足,需要返國。
Thumbnail
金句不適用於自己時,當作借鏡;得到適用於自己的金句,那便是撿到寶囉!
Thumbnail
是不是太美麗的生命總是轉瞬即逝?如同《玫瑰少年》裡說的,「哪種美麗會換來妒忌?」或許是不合時宜的,深埋在靈魂裡的美麗。 談到這個問題,就不得不提起耳熟能詳的葉永鋕。 相信大家對他的死因並不陌生,都知道他是因為「小便之後急著要回到教室,步下台階時卻遇地板濕滑而重心失衡,引發迷走神經性昏厥並倒地,頭
Thumbnail
最近碰到一個專業人士,他有使用Google商家,Google商家裡面有些關於他的負評。 這次遇到他,他提及幾年前的負評內容... 留下負評的人可能是一時情緒上頭,幾年後大概也忘了,但被評論的人,還記得並且為此苦惱。 坦白說,從業快10多年,完全沒有負評是真的很厲害,但有負評也是挺正常的,畢竟人與人之
Thumbnail
「摘要力」指的是抓住資訊重點,因應狀況簡潔且有邏輯的表達自身想法的能力。簡單來說就是要找到「臨死前最想說的話」
Thumbnail
台積電1月合併營收突破天際!高達1,721億元!刺激股價大漲2.53%來到649元,離1月高點683只有5%的距離。還有上漲的動能嗎?Mr. S綜合上月法說指引及近期新聞後,想跟你分享營收創新高,背後所透露的3大利多。 趕緊來閱讀文章吧! 利多1:營收創新高-可望打破第一季指引
Thumbnail
《下輩子我再好好過》絕對不是一部傳統的愛情喜劇,光是女主角小桃那五個砲友的設定,以及不同於一般愛情的價值觀,就已經註定這部作品會是內田理央未來仍舊被不斷提起的代表作之一,老實說我甚至沒想過這部片居然能出第二季!
Thumbnail
要讓自我的自尊安穩,不任由他人的損害及抨擊,就需要注意是否能護衛自我的情感心理空間。也就是,你有獨立的情緒感受歷程,能允許自己有個體的感受和想法,這些都不需得到他人認同才能存在。 同時,不因情緒界限混淆、鬆散,讓他人的情緒輕易就能侵入到你的內在空間,佔據你的心理、影響你的生理....
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
京畿道烏山市的韓神大學近期因將烏茲別克留學生強制送出境引起爭議。 11月27日,韓神大學校方告知23位就讀 #語學堂 的烏茲別克學生,為領取 #外國人登錄證,須共同搭乘巴士前往出入境管理所。然而,該巴士卻在保全業者的護送下,開往仁川國際機場。教職員們這才告訴學生,這些學生的居留條件不足,需要返國。
Thumbnail
金句不適用於自己時,當作借鏡;得到適用於自己的金句,那便是撿到寶囉!
Thumbnail
是不是太美麗的生命總是轉瞬即逝?如同《玫瑰少年》裡說的,「哪種美麗會換來妒忌?」或許是不合時宜的,深埋在靈魂裡的美麗。 談到這個問題,就不得不提起耳熟能詳的葉永鋕。 相信大家對他的死因並不陌生,都知道他是因為「小便之後急著要回到教室,步下台階時卻遇地板濕滑而重心失衡,引發迷走神經性昏厥並倒地,頭
Thumbnail
最近碰到一個專業人士,他有使用Google商家,Google商家裡面有些關於他的負評。 這次遇到他,他提及幾年前的負評內容... 留下負評的人可能是一時情緒上頭,幾年後大概也忘了,但被評論的人,還記得並且為此苦惱。 坦白說,從業快10多年,完全沒有負評是真的很厲害,但有負評也是挺正常的,畢竟人與人之
Thumbnail
「摘要力」指的是抓住資訊重點,因應狀況簡潔且有邏輯的表達自身想法的能力。簡單來說就是要找到「臨死前最想說的話」
Thumbnail
台積電1月合併營收突破天際!高達1,721億元!刺激股價大漲2.53%來到649元,離1月高點683只有5%的距離。還有上漲的動能嗎?Mr. S綜合上月法說指引及近期新聞後,想跟你分享營收創新高,背後所透露的3大利多。 趕緊來閱讀文章吧! 利多1:營收創新高-可望打破第一季指引
Thumbnail
《下輩子我再好好過》絕對不是一部傳統的愛情喜劇,光是女主角小桃那五個砲友的設定,以及不同於一般愛情的價值觀,就已經註定這部作品會是內田理央未來仍舊被不斷提起的代表作之一,老實說我甚至沒想過這部片居然能出第二季!
Thumbnail
要讓自我的自尊安穩,不任由他人的損害及抨擊,就需要注意是否能護衛自我的情感心理空間。也就是,你有獨立的情緒感受歷程,能允許自己有個體的感受和想法,這些都不需得到他人認同才能存在。 同時,不因情緒界限混淆、鬆散,讓他人的情緒輕易就能侵入到你的內在空間,佔據你的心理、影響你的生理....