前言
雖然之前就知道 JavaScript 浮點數的問題,想不到居然在今天遇到了。這次的問題是因為專案中使用了 .toFixed()
這個 JavaScript 原生的方法,導致在進位時出現了一些問題。還好之前上課有認真,看到問題的第一時間就知道可能會是這樣,就往這方面去查,結果也如我預期的。
浮點數的問題
先來解釋什麼是 JavaScript 浮點數的問題:
1 | console.log("輸出: ", 0.1 + 0.2); |
我相信你把這個拿給任何一個會加法的人都會跟你說答案是 0.3,但為什麼執行結果會不一樣呢?
這是因為我們平常在日常生活中使用的數字是叫做十進制,但電腦卻是使用二進制來存儲數字。當電腦要計算 0.1 + 0.2 時必須把這兩個數字轉換成二進制,但並不是所有的十進制數字都能被精確地表示為二進制。
.toFixed() 方法的局限性
.toFixed()
方法可以控制小數位數的顯示,但它並非處理 JavaScript 浮點數問題的完美解決方案。
基本用法:
1 | let num = 0.1 + 0.2; |
看起來很完美,但實際上還是有問題:
1 | console.log((1.005).toFixed(2)); // 輸出:"1.00"而不是預期的"1.01" |
補充:.toFixed()
返回的是字符串,不是數字:
1 | let result = (0.1 + 0.2).toFixed(2); |
因此,如果要進行進一步的數值運算,必須將結果轉回數字。
解決方法
之前我最常用的方式就是使用 Math.round()
這個方法:
1 | function roundToTwo(num) { |
簡單介紹 Math.round()
這個方法:它是將數字四捨五入到最接近的整數。
- 如果小數部分小於 0.5,向下取整。
- 如果小數部分大於或等於 0.5,向上取整。
這就很像我們直覺的四捨五入。
但我們總是要進步的,不能永遠吃老本。以下是一些其他解決方案:
Number.prototype.toPrecision()
toPrecision()
方法返回字串,可以搭配 parseFloat()
方法來轉換成數字:
1 | let num = 123.456; |
適用於需要控制整體精度的場景,但注意它同樣返回字串。
Intl.NumberFormat
Intl.NumberFormat
是 JavaScript 國際化 API (Internationalization API) 的一部分,用於根據特定語言和地區的規則格式化數字:
1 | // 基本語法 |
options
常用的選項:
style
:設置數字的顯示樣式(可選值:’decimal’ | ‘currency’ | ‘percent’ | ‘unit’)minimumFractionDigits
:最少要顯示的小數位數maximumFractionDigits
:最多能顯示的小數位數useGrouping
:是否使用千分位分隔符
但需要注意,它僅適合顯示,不適合精確計算。
總結
在處理 JavaScript 浮點數時,我們可以根據不同的場景選擇適合的解決方案:
- 一般場景:使用
Math.round()
配合乘除法 - 需要格式化顯示:使用
Intl.NumberFormat
- 特定精度需求:使用
toPrecision()
方法 - 需要精確計算:考慮使用專業的數學函式庫
最重要的是要理解 JavaScript 中浮點數的特性,在開發時預先考慮可能遇到的精度問題,選擇合適的解決方案。特別是在處理金額等敏感數據時,更要注意精度問題,確保計算結果的準確性。