JavaScript解決浮點(diǎn)數(shù)運(yùn)算精度丟失問題的幾種方案
場景復(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

那么為什么會這樣呢?
這是因?yàn)?JavaScript 內(nèi)部的數(shù)字均以 IEEE 754 標(biāo)準(zhǔn)的雙精度浮點(diǎn)數(shù)格式存儲,這種格式只能精確表示有限個(gè)小數(shù),而對于一些無限循環(huán)小數(shù)或無理數(shù),無法精確表示,就會出現(xiàn)精度丟失的情況。
有一些前端的面試題就會問你 為什么0.1+0.2的結(jié)果不等于0.3

總之, 前端計(jì)算存在這么一個(gè)問題。
幾種解決方案對比
轉(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ù)放大之后會發(fā)現(xiàn)還是有問題
字符串模擬運(yùn)算
這種方式是目前最為靠譜的一種方式,從源頭上解決了這個(gè)問題。
很多處理浮點(diǎn)數(shù)相關(guān)的第三方庫實(shí)現(xiàn)原理都是基于這種方式來實(shí)現(xiàn)的
第三方庫
Math.js:一個(gè)用于 JavaScript 和 Node.js 的擴(kuò)展數(shù)學(xué)庫decimal.js: 計(jì)算任意精度的十進(jìn)制類型。big.js:一個(gè)小型,快速,易于使用的庫,用于任意精度的十進(jìn)制算術(shù)運(yùn)算。bignumber.js: 一個(gè)用于任意精度算術(shù)的 JavaScript 庫。
這些第三方庫都可以解決這個(gè)問題
bignumber.js的使用
這邊我以這個(gè)庫為例來解決這個(gè)問題,這個(gè)庫的解決原理就是前面提到的字符串模擬運(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ā)中最好都是直接用第三方庫去處理,避免出現(xiàn)計(jì)算錯(cuò)誤之類的問題,導(dǎo)致背大鍋。
到此這篇關(guān)于JavaScript解決浮點(diǎn)數(shù)運(yùn)算精度丟失問題的幾種方案的文章就介紹到這了,更多相關(guān)JavaScript浮點(diǎn)數(shù)運(yùn)算精度丟失內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript中valueOf函數(shù)與toString方法深入理解
基本上,所有JS數(shù)據(jù)類型都擁有valueOf和toString這兩個(gè)方法,null除外。它們倆解決javascript值運(yùn)算與顯示的問題,本文將詳細(xì)介紹,有需要的朋友可以參考下2012-12-12
JS實(shí)現(xiàn)勻速與減速緩慢運(yùn)動(dòng)的動(dòng)畫效果封裝示例
這篇文章主要介紹了JS實(shí)現(xiàn)勻速與減速緩慢運(yùn)動(dòng)的動(dòng)畫效果,結(jié)合實(shí)例形式分析了JavaScript封裝結(jié)合定時(shí)器的頁面元素動(dòng)態(tài)變換效果動(dòng)畫相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
JavaScript中日期函數(shù)的相關(guān)操作知識
日期函數(shù)是我們經(jīng)常用到的知識點(diǎn),下面通過本文給大家介紹JavaScript中日期函數(shù)的相關(guān)操作知識,非常不錯(cuò),感興趣的朋友一起學(xué)習(xí)吧2016-08-08
JS中關(guān)于ES6?Module模塊化的跨域報(bào)錯(cuò)問題解決
這篇文章主要介紹了JS中關(guān)于ES6?Module模塊化的跨域報(bào)錯(cuò),ES6模塊化提供了export命令和import?命令,對于模塊的導(dǎo)出和引入,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
js獲取json中key所對應(yīng)的value值的簡單方法
下面小編就為大家?guī)硪黄猨s獲取json中key所對應(yīng)的value值的簡單方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-03-03
前端實(shí)現(xiàn)獲取后端返回的文件流并下載三種方法
文件下載是最常見的流形式返回?cái)?shù)據(jù)的應(yīng)用場景之一,通過流形式返回文件內(nèi)容,可以避免將整個(gè)文件加載到內(nèi)存中,從而減少內(nèi)存消耗,這篇文章主要介紹了前端實(shí)現(xiàn)獲取后端返回的文件流并下載的三種方法,需要的朋友可以參考下2025-04-04
uniapp實(shí)現(xiàn)微信小程序一鍵登錄功能
這篇文章給大家介紹了uniapp實(shí)現(xiàn)微信小程序一鍵登錄功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-10-10
微信小程序?qū)崿F(xiàn)地區(qū)選擇偽五級聯(lián)動(dòng)
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)地區(qū)選擇偽五級聯(lián)動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07

