JavaScript 浮點(diǎn)數(shù)精度問題小結(jié)
一. 引言
浮點(diǎn)數(shù)精度問題是指在計(jì)算機(jī)中使用二進(jìn)制表示浮點(diǎn)數(shù)時(shí),由于二進(jìn)制無法精確表示某些十進(jìn)制小數(shù),導(dǎo)致計(jì)算結(jié)果可能存在舍入誤差或不精確的情況。
這個(gè)問題主要源于浮點(diǎn)數(shù)的存儲(chǔ)方式。在計(jì)算機(jī)中,浮點(diǎn)數(shù)通常使用IEEE 754標(biāo)準(zhǔn)來表示。該標(biāo)準(zhǔn)將浮點(diǎn)數(shù)分為符號(hào)位、指數(shù)位和尾數(shù)位,使用科學(xué)計(jì)數(shù)法來表示一個(gè)浮點(diǎn)數(shù)。
二. 常見浮點(diǎn)數(shù)精度問題
1. 示例一:浮點(diǎn)值運(yùn)算結(jié)果
// 加法 console.log(0.1 + 0.2); // 0.30000000000000004 console.log(0.7 + 0.1); // 0.7999999999999999 console.log(0.2 + 0.4); // 0.6000000000000001 console.log(2.22 + 0.1); // 2.3200000000000003 // 減法 console.log(1.5 - 1.2); // 0.30000000000000004 console.log(0.3 - 0.2); // 0.09999999999999998 // 乘法 console.log(19.9 * 100); // 1989.9999999999998 console.log(19.9 * 10 * 10); // 1990 console.log(9.7 * 100); // 969.9999999999999 console.log(39.7 * 100); // 3970.0000000000005 // 除法 console.log(0.3 / 0.1); // 2.9999999999999996 console.log(0.69 / 10); // 0.06899999999999999
運(yùn)行結(jié)果如下圖所示:

2. 示例二:小數(shù)乘以10的n次方取整(比如:元轉(zhuǎn)分,米轉(zhuǎn)厘米)
console.log(parseInt(0.58 * 100, 10)); // 57
在上面的例子中,我們得出的結(jié)果是 57,而不是預(yù)期結(jié)果 58。
3. 示例三:四舍五入保留 n 位小數(shù)
例如我們會(huì)寫出 (number).toFixed(2) ,但是看下面的例子:
console.log((1.335).toFixed(2)); // 1.33
在上面的例子中,我們得出的結(jié)果是 1.33,而不是預(yù)期結(jié)果 1.34。
三. 為什么會(huì)出現(xiàn)這樣的結(jié)果
1. 浮點(diǎn)數(shù)表示
在計(jì)算機(jī)中,浮點(diǎn)數(shù)通常使用IEEE 754標(biāo)準(zhǔn)來表示。該標(biāo)準(zhǔn)將浮點(diǎn)數(shù)分為符號(hào)位、指數(shù)位和尾數(shù)位,使用科學(xué)計(jì)數(shù)法來表示一個(gè)浮點(diǎn)數(shù)。
該規(guī)范定義了浮點(diǎn)數(shù)的格式,對(duì)于 64 位的浮點(diǎn)數(shù)在內(nèi)存中的表示,最高的 1 位是符號(hào)位,接著的 11 位是指數(shù),剩下的 52 位為有效數(shù)字,具體表示方式如下:
符號(hào)位 S:用于表示正負(fù)號(hào)。第 1 位是正負(fù)數(shù)符號(hào)位(sign),0 代表正數(shù),1 代表負(fù)數(shù)。
指數(shù)位 E:用于表示浮點(diǎn)數(shù)的指數(shù)部分,以二進(jìn)制補(bǔ)碼形式存儲(chǔ)。中間的 11 位存儲(chǔ)指數(shù)(exponent),用來表示次方數(shù)。
尾數(shù)位 M:用于表示浮點(diǎn)數(shù)的有效數(shù)字部分,以二進(jìn)制形式存儲(chǔ)。最后的 52 位是尾數(shù)(mantissa),儲(chǔ)存小數(shù)部分,超出的部分自動(dòng)進(jìn)一舍零。
即:浮點(diǎn)數(shù)最終在運(yùn)算的時(shí)候?qū)嶋H上是一個(gè)符合該標(biāo)準(zhǔn)的二進(jìn)制數(shù)
符號(hào)位決定了一個(gè)數(shù)的正負(fù),指數(shù)部分決定了數(shù)值的大小,小數(shù)部分決定了數(shù)值的精度。
IEEE 754 規(guī)定,有效數(shù)字第一位默認(rèn)總是 1,不保存在 64 位浮點(diǎn)數(shù)之中。也就是說,有效數(shù)字總是 1.xx…xx 的形式,其中 xx…xx 的部分保存在 64 位浮點(diǎn)數(shù)之中,最長(zhǎng)可能為 52 位。因此,JavaScript 提供的有效數(shù)字最長(zhǎng)為 53 個(gè)二進(jìn)制位(64 位浮點(diǎn)的后 52 位 + 有效數(shù)字第一位的 1)。
既然限定位數(shù),必然有截?cái)嗟目赡堋?/p>
2. 舉例說明
示例一
console.log(0.1 + 0.2); // 0.30000000000000004
為了驗(yàn)證該例子,我們得先知道怎么將浮點(diǎn)數(shù)轉(zhuǎn)換為二進(jìn)制,整數(shù)我們可以用除 2 取余的方式,小數(shù)我們則可以用乘 2 取整的方式。
*0.1* 轉(zhuǎn)換為二進(jìn)制
0.1 * 2,值為 0.2,小數(shù)部分 0.2,整數(shù)部分 0
0.2 * 2,值為 0.4,小數(shù)部分 0.4,整數(shù)部分 0
0.4 * 2,值為 0.8,小數(shù)部分 0.8,整數(shù)部分 0
0.8 * 2,值為 1.6,小數(shù)部分 0.6,整數(shù)部分 1
0.6 * 2,值為 1.2,小數(shù)部分 0.2,整數(shù)部分 1
0.2 * 2,值為 0.4,小數(shù)部分 0.4,整數(shù)部分 0
從 0.2 開始循環(huán)
*0.2* 轉(zhuǎn)換為二進(jìn)制
可以直接參考上述,肯定最后也是一個(gè)循環(huán)的情況
所以最終我們能得到兩個(gè)循環(huán)的二進(jìn)制數(shù):
0.1:0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1100 ...
0.2:0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 ...
這兩個(gè)的和的二進(jìn)制就是:
sum:0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ...
最終我們只能得到和的近似值(按照 IEEE 754 標(biāo)準(zhǔn)保留 52 位,按 0 舍 1 入來取值),然后轉(zhuǎn)換為十進(jìn)制數(shù)變成:
sum ≈ 0.30000000000000004
示例二
console.log((1.335).toFixed(2)); // 1.33
因?yàn)?nbsp;1.335 其實(shí)是 1.33499999999999996447286321199,toFixed 雖然是四舍五入,但是是對(duì) 1.33499999999999996447286321199 進(jìn)行四五入,所以得出 1.33。
示例三
在 Javascript 中,整數(shù)精度同樣存在問題,先來看看問題:
console.log(19571992547450991) // 19571992547450990 console.log(19571992547450991 === 19571992547450992) // true
如下圖所示:

同樣的原因,在 JavaScript 中 number 類型統(tǒng)一按浮點(diǎn)數(shù)處理,整數(shù)是按最大 54 位來算。
最大( 253 - 1,Number.MAX_SAFE_INTEGER、9007199254740991)
最小( -(253 - 1) ,Number.MIN_SAFE_INTEGER、*-9007199254740991*)
在浮點(diǎn)數(shù)計(jì)算中,有一些特定的小數(shù)情況可以避免舍入誤差。這是因?yàn)檫@些特定的小數(shù)可以精確地表示為二進(jìn)制分?jǐn)?shù),而不會(huì)導(dǎo)致舍入誤差。以下是一些常見的特定情況:
小數(shù)部分是2的負(fù)整數(shù)次冪:例如,0.5、0.25、0.125等。這些小數(shù)在二進(jìn)制中可以精確表示,因此計(jì)算時(shí)不會(huì)出現(xiàn)舍入誤差。
小數(shù)部分是10的負(fù)整數(shù)次冪:例如,0.1、0.01、0.001等。盡管在十進(jìn)制中無法精確表示,但在二進(jìn)制中可以通過有限位數(shù)進(jìn)行近似表示,并且通常不會(huì)引起明顯的舍入誤差。
四. 總結(jié)
浮點(diǎn)數(shù)精度問題是計(jì)算機(jī)科學(xué)中一個(gè)常見的問題,由于二進(jìn)制無法精確表示某些十進(jìn)制小數(shù),進(jìn)行浮點(diǎn)數(shù)運(yùn)算時(shí)可能會(huì)出現(xiàn)舍入誤差。為了解決這個(gè)問題,可以使用整數(shù)進(jìn)行計(jì)算、使用專門的庫(kù)或者比較時(shí)使用誤差范圍。了解浮點(diǎn)數(shù)精度問題對(duì)于開發(fā)人員在處理浮點(diǎn)數(shù)運(yùn)算時(shí)具有重要意義。
到此這篇關(guān)于JavaScript 浮點(diǎn)數(shù)精度問題小結(jié)的文章就介紹到這了,更多相關(guān)JavaScript 浮點(diǎn)數(shù)精度內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- javascript將浮點(diǎn)數(shù)轉(zhuǎn)換成整數(shù)的三個(gè)方法
- 跟我學(xué)習(xí)javascript的浮點(diǎn)數(shù)精度
- 深入理解JavaScript中的浮點(diǎn)數(shù)
- JavaScript浮點(diǎn)數(shù)及運(yùn)算精度調(diào)整詳解
- 詳解JS-- 浮點(diǎn)數(shù)運(yùn)算處理
- JavaScript解決浮點(diǎn)數(shù)計(jì)算不準(zhǔn)確問題的方法分析
- 詳解JavaScript 浮點(diǎn)數(shù)運(yùn)算的精度問題
- JS實(shí)現(xiàn)的進(jìn)制轉(zhuǎn)換,浮點(diǎn)數(shù)相加,數(shù)字判斷操作示例
- JS中浮點(diǎn)數(shù)精度問題的分析與解決方法
- 處理JavaScript浮點(diǎn)數(shù)精度問題的解決方案
相關(guān)文章
JavaScript循環(huán)遍歷的24個(gè)方法,你都知道嗎
這篇文章主要給大家介紹了關(guān)于JavaScript循環(huán)遍歷的24個(gè)方法,文中對(duì)每種方法都給出了詳細(xì)的實(shí)例代碼,方便大家理解學(xué)習(xí),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-09-09
JavaScript實(shí)現(xiàn)簡(jiǎn)單精致的圖片左右無縫滾動(dòng)效果
這篇文章主要介紹了JavaScript實(shí)現(xiàn)簡(jiǎn)單精致的圖片左右無縫滾動(dòng)效果,涉及javascript結(jié)合時(shí)間函數(shù)動(dòng)態(tài)操作頁面元素屬性的相關(guān)技巧,需要的朋友可以參考下2017-03-03
ES6中Set和Map數(shù)據(jù)結(jié)構(gòu)的簡(jiǎn)單講解
大家心里是否產(chǎn)生過這樣的疑問,JS中既然已經(jīng)有對(duì)象這種數(shù)據(jù)結(jié)構(gòu),我們?yōu)槭裁催€要再單獨(dú)去使用Set或者M(jìn)ap呢?下面這篇文章主要給大家介紹了關(guān)于ES6中Set和Map數(shù)據(jù)結(jié)構(gòu)的相關(guān)資料,需要的朋友可以參考下2022-08-08
js 數(shù)組隨機(jī)字符串(廣告不重復(fù))
今天一個(gè)網(wǎng)友想讓他的廣告隨機(jī)顯示,每次刷新廣告的內(nèi)容都不一樣,經(jīng)過參考源碼網(wǎng)站分析就是通過下面代碼實(shí)現(xiàn),特分享下方便需要的朋友2013-08-08
javascript設(shè)計(jì)模式 – 外觀模式原理與用法實(shí)例分析
這篇文章主要介紹了javascript設(shè)計(jì)模式 – 外觀模式,結(jié)合實(shí)例形式分析了javascript外觀模式基本概念、原理、用法及操作注意事項(xiàng),需要的朋友可以參考下2020-04-04
js解決pdf使用iframe打印報(bào)跨域錯(cuò)誤問題的方法示例
這篇文章主要給大家介紹了關(guān)于js解決pdf使用iframe打印報(bào)跨域錯(cuò)誤問題的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用js具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-03-03

