最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS浮點(diǎn)數(shù)精度問題及解決過程

 更新時(shí)間:2025年07月27日 15:18:28   作者:琢鳴  
文章解析JavaScript浮點(diǎn)數(shù)精度問題,因IEEE754雙精度存儲(chǔ)導(dǎo)致,如0.2+0.1不等于0.3,建議使用Math.js、Decimal.js等庫處理,高精度計(jì)算宜交后端

經(jīng)典真題

  • 為什么 console.log(0.2+0.1==0.3) 得到的值為 false

浮點(diǎn)數(shù)精度常見問題

在 JavaScript 中整數(shù)和浮點(diǎn)數(shù)都屬于 number 數(shù)據(jù)類型,所有數(shù)字都是以 64 位浮點(diǎn)數(shù)形式儲(chǔ)存,即便整數(shù)也是如此。 所以我們?cè)诖蛴?nbsp;1.00 這樣的浮點(diǎn)數(shù)的結(jié)果是 1 而非 1.00 。

在一些特殊的數(shù)值表示中,例如金額,這樣看上去有點(diǎn)別扭,但是至少值是正確了。

然而要命的是,當(dāng)浮點(diǎn)數(shù)做數(shù)學(xué)運(yùn)算的時(shí)候,你經(jīng)常會(huì)發(fā)現(xiàn)一些問題,舉幾個(gè)例子:

場景一:進(jìn)行浮點(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

場景二:將小數(shù)乘以 10 的 n 次方取整

比如將錢幣的單位,從元轉(zhuǎn)化成分,經(jīng)常寫出來的是 parseInt(yuan*100, 10)

console.log(parseInt(0.58 * 100, 10)); // 57

場景三:四舍五入保留 n 位小數(shù)

例如我們會(huì)寫出 (number).toFixed(2),但是看下面的例子:

console.log((1.335).toFixed(2)); // 1.33

在上面的例子中,我們得出的結(jié)果是 1.33,而不是預(yù)期結(jié)果 1.34。

為什么會(huì)有這樣的問題

似乎是不可思議。小學(xué)生都會(huì)算的題目,JavaScript 不會(huì)?

我們來看看其真正的原因,到底為什么會(huì)產(chǎn)生精度丟失的問題呢?

計(jì)算機(jī)底層只有 0 和 1, 所以所有的運(yùn)算最后實(shí)際上都是二進(jìn)制運(yùn)算。

十進(jìn)制整數(shù)利用輾轉(zhuǎn)相除的方法可以準(zhǔn)確地轉(zhuǎn)換為二進(jìn)制數(shù),但浮點(diǎn)數(shù)呢?

JavaScript 里的數(shù)字是采用 IEEE 754 標(biāo)準(zhǔn)的 64 位雙精度浮點(diǎn)數(shù)。

先看下面一張圖:

該規(guī)范定義了浮點(diǎn)數(shù)的格式,對(duì)于 64 位的浮點(diǎn)數(shù)在內(nèi)存中的表示,最高的 1 位是符號(hào)位,接著的 11 位是指數(shù),剩下的 52 位為有效數(shù)字,具體如下:

  • 符號(hào)位 S:第 1 位是正負(fù)數(shù)符號(hào)位(sign),0 代表正數(shù),1 代表負(fù)數(shù)
  • 指數(shù)位 E:中間的 11 位存儲(chǔ)指數(shù)(exponent),用來表示次方數(shù)
  • 尾數(shù)位 M:最后的 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ù)之中,最長可能為 52 位。因此,JavaScript 提供的有效數(shù)字最長為 53 個(gè)二進(jìn)制位(64 位浮點(diǎn)的后 52 位 + 有效數(shù)字第一位的 1)。

既然限定位數(shù),必然有截?cái)嗟目赡堋?/p>

我們可以看一個(gè)例子:

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)

所以只要超過這個(gè)范圍,就會(huì)存在被舍去的精度問題。

當(dāng)然這個(gè)問題并不只是在 Javascript 中才會(huì)出現(xiàn),幾乎所有的編程語言都采用了 IEEE-754 浮點(diǎn)數(shù)表示法,任何使用二進(jìn)制浮點(diǎn)數(shù)的編程語言都會(huì)有這個(gè)問題。

只不過在很多其他語言中已經(jīng)封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設(shè)計(jì)思想上就沒有對(duì)浮點(diǎn)數(shù)有個(gè)嚴(yán)格的數(shù)據(jù)類型,所以精度誤差的問題就顯得格外突出。

通常這種對(duì)精度要求高的計(jì)算都應(yīng)該交給后端去計(jì)算和存儲(chǔ),因?yàn)楹蠖擞谐墒斓膸靵斫鉀Q這種計(jì)算問題。

前端也有幾個(gè)不錯(cuò)的類庫:

Math.js

  • Math.js 是專門為 JavaScript 和 Node.js 提供的一個(gè)廣泛的數(shù)學(xué)庫。它具有靈活的表達(dá)式解析器,支持符號(hào)計(jì)算,配有大量內(nèi)置函數(shù)和常量,并提供集成解決方案來處理不同的數(shù)據(jù)類型。
  • 像數(shù)字,大數(shù)字(超出安全數(shù)的數(shù)字),復(fù)數(shù),分?jǐn)?shù),單位和矩陣。 功能強(qiáng)大,易于使用。

decimal.js

  • 為 JavaScript 提供十進(jìn)制類型的任意精度數(shù)值。

big.js

  • 不僅能夠支持處理 Long 類型的數(shù)據(jù),也能夠準(zhǔn)確的處理小數(shù)的運(yùn)算。

真題解答

  • 為什么 console.log(0.2+0.1==0.3) 得到的值為 false

參考答案:

  • 因?yàn)楦↑c(diǎn)數(shù)的計(jì)算存在 round-off 問題,也就是浮點(diǎn)數(shù)不能夠進(jìn)行精確的計(jì)算。并且:
  • 不僅 JavaScript,所有遵循 IEEE 754 規(guī)范的語言都是如此;
  • 在 JavaScript 中,所有的 Number 都是以 64-bit 的雙精度浮點(diǎn)數(shù)存儲(chǔ)的;

雙精度的浮點(diǎn)數(shù)在這 64 位上劃分為 3 段,而這 3 段也就確定了一個(gè)浮點(diǎn)數(shù)的值,64bit 的劃分是“1-11-52”的模式,具體來說:

  • 就是 1 位最高位(最左邊那一位)表示符號(hào)位,0 表示正,1 表示負(fù);
  • 11 位表示指數(shù)部分;
  • 52 位表示尾數(shù)部分,也就是有效域部分

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JavaScript構(gòu)造函數(shù)原理及實(shí)現(xiàn)流程解析

    JavaScript構(gòu)造函數(shù)原理及實(shí)現(xiàn)流程解析

    這篇文章主要介紹了JavaScript構(gòu)造函數(shù)原理及實(shí)現(xiàn)流程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 在js代碼拼接dom對(duì)象到頁面上去的模板總結(jié)(必看)

    在js代碼拼接dom對(duì)象到頁面上去的模板總結(jié)(必看)

    下面小編就為大家?guī)硪黄趈s代碼拼接dom對(duì)象到頁面上去的模板總結(jié)(必看)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • JavaScript高級(jí)?ES7-ES13?新特性詳解

    JavaScript高級(jí)?ES7-ES13?新特性詳解

    這篇文章主要介紹了JavaScript高級(jí)?ES7-ES13?新特性詳解,本文結(jié)合實(shí)例代碼給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • 微信小程序訂閱消息(java后端實(shí)現(xiàn))開發(fā)

    微信小程序訂閱消息(java后端實(shí)現(xiàn))開發(fā)

    這篇文章主要介紹了微信小程序訂閱消息(java后端實(shí)現(xiàn))開發(fā),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • javascript中自定義對(duì)象的屬性方法分享

    javascript中自定義對(duì)象的屬性方法分享

    這篇文章介紹了在javascript中自定義對(duì)象的屬性方法,有需要的朋友可以參考一下
    2013-07-07
  • JavaScript數(shù)組Array的一些常用方法總結(jié)

    JavaScript數(shù)組Array的一些常用方法總結(jié)

    JavaScript的Array對(duì)象是用于構(gòu)造數(shù)組的全局對(duì)象,數(shù)組是類似于列表的高階對(duì)象,下面這篇文章主要給大家介紹了關(guān)于JavaScript數(shù)組Array的一些常用方法,需要的朋友可以參考下
    2021-11-11
  • 微信小程序全局變量功能與用法詳解

    微信小程序全局變量功能與用法詳解

    這篇文章主要介紹了微信小程序全局變量功能與用法,結(jié)合實(shí)例形式詳細(xì)分析了微信小程序全局變量的作用、定義、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-01-01
  • 來自國外的頁面JavaScript文件優(yōu)化

    來自國外的頁面JavaScript文件優(yōu)化

    外部JavaScript文件塊下載和傷害你的頁面的性能,但有一個(gè)簡單的方法來解決此問題:并行使用動(dòng)態(tài)腳本標(biāo)記和加載腳本,提高頁面加載速度和用戶體驗(yàn)。
    2010-12-12
  • 使用echart實(shí)現(xiàn)柱狀圖里的水波效果示例代碼

    使用echart實(shí)現(xiàn)柱狀圖里的水波效果示例代碼

    這篇文章主要介紹了使用echart實(shí)現(xiàn)柱狀圖里水波效果的相關(guān)資料,主要通過renderItem函數(shù)方法,返回每個(gè)數(shù)據(jù)項(xiàng)對(duì)應(yīng)的圖形元素,包括一個(gè)矩形和一個(gè)波浪路徑,需要的朋友可以參考下
    2026-05-05
  • 詳解將微信小程序接口Promise化并使用async函數(shù)

    詳解將微信小程序接口Promise化并使用async函數(shù)

    這篇文章主要介紹了詳解將微信小程序接口Promise化并使用async函數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08

最新評(píng)論

泾阳县| 榕江县| 社旗县| 彭泽县| 嵊州市| 会东县| 靖远县| 罗甸县| 湖口县| 辽源市| 隆化县| 民丰县| 金溪县| 嵊州市| 合作市| 华容县| 湘潭县| 晋中市| 乐陵市| 高雄市| 会同县| 张掖市| 屯留县| 郎溪县| 柯坪县| 崇文区| 长春市| 泰宁县| 余干县| 平乡县| 平阳县| 昌都县| 和静县| 岫岩| 罗城| 泾阳县| 夏河县| 裕民县| 兰西县| 清流县| 嘉定区|