JavaScript解決浮點(diǎn)數(shù)運(yùn)算精度丟失問題的幾種方案
場(chǎng)景復(fù)現(xiàn)
const value1 = 15966.2 + 3103.3 + 1562.9 + 933.9 console.log('?? ~ value1:', value1)
上面的這些數(shù)值相加的結(jié)果應(yīng)該是21566.3
但是在瀏覽器中運(yùn)算的時(shí)候,結(jié)果卻變成了21566.300000000003
那么為什么會(huì)這樣呢?
這是因?yàn)?JavaScript 內(nèi)部的數(shù)字均以 IEEE 754 標(biāo)準(zhǔn)的雙精度浮點(diǎn)數(shù)格式存儲(chǔ),這種格式只能精確表示有限個(gè)小數(shù),而對(duì)于一些無限循環(huán)小數(shù)或無理數(shù),無法精確表示,就會(huì)出現(xiàn)精度丟失的情況。
有一些前端的面試題就會(huì)問你 為什么0.1+0.2
的結(jié)果不等于0.3
總之, 前端計(jì)算存在這么一個(gè)問題。
幾種解決方案對(duì)比
轉(zhuǎn)化為整數(shù)計(jì)算
function addDecimals(num1, num2) { const multiplier = Math.pow( 10, Math.max( num1.toString().split('.')[1]?.length || 0, num2.toString().split('.')[1]?.length || 0 ) ) return ( (Math.round(num1 * multiplier) + Math.round(num2 * multiplier)) / multiplier ) } console.log(addDecimals(0.1, 0.2)) // 0.3 console.log(addDecimals(0.15, 0.15)) // 0.3
這種方只適合小數(shù)量計(jì)算,大數(shù)還是不行
用toFixed()格式化
這種方式?jīng)]有什么意義,只能展示的時(shí)候用,并且也沒有解決這個(gè)問題
位數(shù)放大之后會(huì)發(fā)現(xiàn)還是有問題
字符串模擬運(yùn)算
這種方式是目前最為靠譜的一種方式,從源頭上解決了這個(gè)問題。
很多處理浮點(diǎn)數(shù)相關(guān)的第三方庫(kù)實(shí)現(xiàn)原理都是基于這種方式來實(shí)現(xiàn)的
第三方庫(kù)
Math.js
:一個(gè)用于 JavaScript 和 Node.js 的擴(kuò)展數(shù)學(xué)庫(kù)decimal.js
: 計(jì)算任意精度的十進(jìn)制類型。big.js
:一個(gè)小型,快速,易于使用的庫(kù),用于任意精度的十進(jìn)制算術(shù)運(yùn)算。bignumber.js
: 一個(gè)用于任意精度算術(shù)的 JavaScript 庫(kù)。
這些第三方庫(kù)都可以解決這個(gè)問題
bignumber.js的使用
這邊我以這個(gè)庫(kù)為例來解決這個(gè)問題,這個(gè)庫(kù)的解決原理就是前面提到的字符串模擬運(yùn)算。
安裝
用包管理器安裝
npm install bignumber.js
相加
let sum = new BigNumber(0) const numbers = [15966.2, 3103.3, 1562.9, 933.9] numbers.forEach((value) => (sum = sum.plus(value))) console.log('?? ~ numbers:', sum.toString())
結(jié)果如下:
相減
const numbers = [15966.2, 3103.3, 1562.9, 933.9] const value1 = new BigNumber(15966.2) console.log('?? ~ value1:', value1.minus(1.1231).toString())
結(jié)果如下:
相乘
const value1 = new BigNumber(15966.2) console.log('?? ~ value1:', value1.multipliedBy(21).toString())
結(jié)果如下:
相除
const value1 = new BigNumber(15966.2) console.log('?? ~ value1:', value1.div(21).toString())
結(jié)果如下:
判斷是否相等和比較大小
const value1 = new BigNumber(15966.2) const value2 = new BigNumber(15966.3) const value3 = new BigNumber(15966.2) const value4 = new BigNumber(15966.1) console.log('15966.2 -> 15966.3 ', value1.comparedTo(value2)) console.log('15966.2 -> 15966.2 ', value1.comparedTo(value3)) console.log('15966.2 -> 15966.1 ', value1.comparedTo(value4))
結(jié)果如下:
-1
: 小于0
: 相等1
: 大于
結(jié)尾
像這種涉及到精度計(jì)算的,開發(fā)中最好都是直接用第三方庫(kù)去處理,避免出現(xiàn)計(jì)算錯(cuò)誤之類的問題,導(dǎo)致背大鍋。
到此這篇關(guān)于JavaScript解決浮點(diǎn)數(shù)運(yùn)算精度丟失問題的幾種方案的文章就介紹到這了,更多相關(guān)JavaScript浮點(diǎn)數(shù)運(yùn)算精度丟失內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解JS數(shù)組Reduce()方法詳解及高級(jí)技巧
reduce 為數(shù)組中的每一個(gè)元素依次執(zhí)行回調(diào)函數(shù),不包括數(shù)組中被刪除或從未被賦值的元素。接下來通過本文給大家分享JS數(shù)組Reduce()方法詳解及高級(jí)技巧,一起看看吧2017-08-08javaScript年份下拉列表框內(nèi)容為當(dāng)前年份及前后50年
本文介紹的這個(gè)javaScript年份下拉列表框內(nèi)容為當(dāng)前年份及前后50年,默認(rèn)顯示當(dāng)前年份,大家可以學(xué)習(xí)下2014-05-05JS實(shí)現(xiàn)數(shù)組去重的11種方法總結(jié)
去重是開發(fā)中經(jīng)常會(huì)碰到的一個(gè)熱點(diǎn)問題,這篇文章主要介紹了JS中實(shí)現(xiàn)數(shù)組去重的11個(gè)方法總結(jié),文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-04-04JS獲取指定月份的天數(shù)兩種實(shí)現(xiàn)方法
這篇文章主要介紹了JS獲取指定月份的天數(shù)兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2018-06-06JavaScript實(shí)現(xiàn)彈出DIV層同時(shí)頁(yè)面背景漸變成半透明效果
這篇文章主要介紹了JavaScript實(shí)現(xiàn)彈出DIV層同時(shí)頁(yè)面背景漸變成半透明效果,涉及JavaScript彈出窗口的實(shí)現(xiàn)及頁(yè)面元素屬性動(dòng)態(tài)變換的相關(guān)技巧,需要的朋友可以參考下2016-03-03JavaScript與Div對(duì)層定位和移動(dòng)獲得坐標(biāo)的實(shí)現(xiàn)代碼
JavaScript與Div對(duì)層定位和移動(dòng)獲得坐標(biāo)的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2010-09-09