如何解決0.1 +0.2===0.30000000000000004類問題

趁你還年輕233發表於2018-12-04

如何解決0.1 +0.2===0.30000000000000004類問題

上篇部落格深度剖析了0.1+0.2 === 0.30000000000000004的原因。 這篇部落格將主要提供幾種解決小數精度丟失問題的Javascript類庫的程式碼示例,以及簡單的原生EcmaScript方法的程式碼示例。

一.類庫部分

math.js

math.js是JavaScript和Node.js的一個廣泛的數學庫。支援數字,大數,複數,分數,單位和矩陣等資料型別的運算。

官網:mathjs.org/ GitHub:github.com/josdejong/m…

0.1+0.2 ===0.3實現程式碼:

var math = require('mathjs')
console.log(math.add(0.1,0.2))//0.30000000000000004
console.log(math.format((math.add(math.bignumber(0.1),math.bignumber(0.2)))))//'0.3'
複製程式碼

decimal.js

為 JavaScript 提供十進位制型別的任意精度數值。

官網:mikemcl.github.io/decimal.js/

GitHub:github.com/MikeMcl/dec…

var Decimal = require('decimal.js')
x = new  Decimal(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'
複製程式碼

bignumber.js

用於任意精度算術的JavaScript庫。

官網:mikemcl.github.io/bignumber.j…

Github:github.com/MikeMcl/big…

var BigNumber = require("bignumber.js")
x = new BigNumber(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'
複製程式碼

big.js

用於任意精度十進位制算術的小型快速JavaScript庫。 官網:mikemcl.github.io/big.js/ Github:github.com/MikeMcl/big…

var Big = require("big.js")
x = new Big(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'
複製程式碼

有一個需要注意的點,使用類庫此時輸出的0.3是String型別,因此若想保持為Number型別,可使用parseFloat()方法。

還有一個注意點,在本地install測試的時候,npm i mathjs -g ,require是也要require('mathjs'),而不是帶點的math.js,因為josdejong這哥們在建立專案的時候就命名為mathjs,而同時擁有上述decimal.js, bignumber.js和big.js的MikeMcl,專案名字就帶了dot,因此安裝和引入時,都是xxx.js的形式。

如何在這三個類庫之間做選擇,還需要大家自己根據具體情況具體分析,我在這裡就不贅述了。

最後,教大家一個線上直接測試的網站,npm.runkit.com,子路徑輸入想要測試的Node.js package名,就可以實現線上測試包中的api了。 例如: math.js:npm.runkit.com/mathjs big.js:npm.runkit.com/big.js

二、原生方法

類庫其實很強大,我們計算0.1+0.2其實只是用到了冰山一角,那麼我們如何使用原生的EcmaScript程式碼來應用於簡單的問題場景呢?

這就要用到Number.prototype.toFixed()這個方法了。

浮點數運算

toFixed() 方法

浮點數運算的解決方案有很多,這裡給出一種目前常用的解決方案, 在判斷浮點數運算結果前對計算結果進行精度縮小,因為在精度縮小的過程總會自動四捨五入。

toFixed() 方法使用定點表示法來格式化一個數,會對結果進行四捨五入。語法為:

JavaScript 程式碼: numObj.toFixed(digits) 引數 digits 表示小數點後數字的個數;介於 0 到 20 (包括)之間,實現環境可能支援更大範圍。如果忽略該引數,則預設為 0。

返回一個數值的字串表現形式,不使用指數記數法,而是在小數點後有 digits 位數字。該數值在必要時進行四捨五入,另外在必要時會用 0 來填充小數部分,以便小數部分有指定的位數。 如果數值大於 1e+21,該方法會簡單呼叫 Number.prototype.toString()並返回一個指數記數法格式的字串。

特別注意:toFixed() 返回一個數值的字串表現形式。

具體可以檢視 MDN中的說明,那麼我們可以這樣解決精度問題:

JavaScript 程式碼:

parseFloat((數學表示式).toFixed(digits)); // toFixed() 精度引數須在 0 與20 之間
// 執行
parseFloat((0.1 + 0.2).toFixed(10))//結果為0.3
parseFloat((0.3 / 0.1).toFixed(10)) // 結果為 3  
parseFloat((0.7 * 180).toFixed(10))//結果為126
parseFloat((1.0 - 0.9).toFixed(10)) // 結果為 0.1   
parseFloat((9.7 * 100).toFixed(10)) // 結果為 970 
parseFloat((2.22 + 0.1).toFixed(10)) // 結果為 2.32
複製程式碼

在Browser環境精度引數允許0~100位之間(包括100),測試版本為Chrome62(64位)和Firefox56 (32 位)。 在Nodejs環境中,只能是0~20之間,測試版本為v6.9.5。

其次就是toFixed()的瀏覽器相容性討論,MDN給出的結果全部是YES,無論desktop端還是mobile端,也就是說不用擔心toFixed()的相容性問題(ie8- 我們不做討論)。

desktop端:

如何解決0.1 +0.2===0.30000000000000004類問題

mobile端:

如何解決0.1 +0.2===0.30000000000000004類問題

Thanks: www.css88.com/archives/73… developer.mozilla.org/zh-CN/docs/…

相關文章