JS中如何理解浮點數?

来源:https://www.cnblogs.com/qcloud1001/archive/2018/12/26/10178391.html
-Advertisement-
Play Games

本文由雲+社區發表 相信大家在平常的 JavaScript 開發中,都有遇到過浮點數運算精度誤差的問題,比如 。在 JavaScript 中,所有的數字包括整數和小數都是用 類型來表示的。本文通過介紹 的二進位存儲標準來理解浮點數運算精度問題,和理解 對象的 等屬性值是如何取值的,最後介紹了一些常用 ...


本文由雲+社區發表

相信大家在平常的 JavaScript 開發中,都有遇到過浮點數運算精度誤差的問題,比如 console.log(0.1+0.2===0.3)// false。在 JavaScript 中,所有的數字包括整數和小數都是用 Number 類型來表示的。本文通過介紹 Number 的二進位存儲標準來理解浮點數運算精度問題,和理解 Number 對象的 MAX_VALUE 等屬性值是如何取值的,最後介紹了一些常用的浮點數精度運算解決方案。

Number 的存儲標準

JavaScript Number 採用的是 IEEE 754 定義的 64 位雙精度浮點型來表示。具體的位元組分配可以先看一下引自維基百科的圖:

img

從上圖中可以看到,從高到低,64位被分成3段,分別是:

  • sign: 符號位,占 1 位;
  • exponent: 指數位,占 11 位;
  • fraction: 有效數字位,占 52 位。

指數位有 11 位,取值範圍是 0 到 2047。當指數位 e=0 或者 e=2017 時,根據有效數字位 f 是否為 0 ,具有不同的特殊含義,具體見下表:

img

對於常用的 normal number, 為了方便表示指數為負數的情況,所以,指數位數值大小做了一個 -1023 的偏移量。對於一個非 0 數字而言,,它的二進位的科學計數法里的第一位有效數字固定是 1。這樣,一個雙精度浮點型數字的值就是

img

對於 subnormal number,它可以用來表示更加接近於 0 的數,它特殊的地方是有效數字位的前面補充的是 0 而不是 1,且指數為偏移量是 -1022,所以值是:

img

Number 對象中的幾個屬性值

知道了 Number 是如何存儲之後,Number 對象的屬性是如何取值的就明朗了。

Number.MAX_VALUE:可表示的最大的數,顯然 e 和 f 都取最大時能表示的數最大,值為

img

Number.MIN_VALUE:可表示的最小的正數,用最小的 subnormal number 來表示。當 e = 0 ,f 的最後一位為 1,其他為 0 時最小,值為

img

Number.EPSILON : 表示 1 與 Number 可表示的大於 1 的最小的浮點數之間的差值。值為

img

Number.MAXSAFEINTEGER:表示在 JavaScript 中最大的安全整數。可以連續且精確被表示出來的整數成為安全整數,比如 2^54 就不是個安全整數,因為它和 2^54+1 兩個數的表示是完全一樣的,e=1077,f=0。 Math.pow(2,54)===Math.pow(2,54)+1// true。整數轉化為二進位後,小數點後是不會有數字的,而用二進位的科學計數法表示時,小數點後最多保留 52 位,加上前置的一個 1,有 53 位數字,所以當一個數轉化二進位時,如果位數超過 53 位,必然會截斷末尾的部分,即導致不能精確表示,即為不安全整數。所以最小的會被截斷的整數是 100...001=2^53+1(中間有52個0)。這個數設為 X,則比 X 小的整數都能被精確表示出來,再加上“連續”這個條件,所以 X-1 不是我們要的答案,X-2 才是。 Number.MAX_SAFE_INTEGER 最終值為

img

Number.MINSAFEINTEGER:表示在 JavaScript 中最小的安全整數,對 Number.MAX_SAFE_INTEGER 取負值即可,值為 -9007199254740991

為什麼0.1+0.2不等於0.3

現在看看 console.log(0.1+0.2===0.3)// false 這個問題,數字 0.1 轉化成二進位是 0.0001100110011... 即 1.10011001...1001 * 2^-4 (小數部分有52位,即有13個1001迴圈)。由於第 53 位是 1,類似 10 進位的四捨五入,二進位是“零舍一入”,所以 0.1 的最終二進位科學計數法表示是 1.10011001...1010 * 2^-4,即二進位數值大小實際上是 0.000110011001...10011010。下麵的代碼驗證了這個值(列印出來的值,把最末尾的0去掉了):

var a = 0.1;console.log(a.toString(2)); //0.0001100110011001100110011001100110011001100110011001101

同理十進位數字 0.2 轉化為二進位的最終值是 1.10011001...1010 * 2^-3 即 0.00110011...100111010;十進位 0.3 轉化位二進位的最終值是 1.00110011...0011 * 2^-2

var b = 0.2;console.log(b.toString(2)); //0.001100110011001100110011001100110011001100110011001101var c = 0.3;console.log(c.toString(2)); //0.010011001100110011001100110011001100110011001100110011

所以,0.1+0.2 的值即為上面 0.1 和 0.2 對應的二進位數值的相加,如下圖所示

img

上圖中,對所得的和,“零舍一入”保留 52 位有效小數就是最終的值:0.01001100...110100(第 53 位是 1 ,所以往前進了 1),如下代碼所示。這個值與上文中的 0.3 的最終二進位表示的值明顯不相同,即解釋了 0.1 + 0.2 不等於 0.3 的根本原因所在(實際上,這個值轉化為 10 進位約等於 0.30000000000000004)。註:列印出來的長度是 54,因為有 52 位有效小數,前面是'0.01',長度是 4,最後去掉末尾的 2 個 0,所以最後列印出來的長度是 52+4-2 = 54。

var d = 0.1 + 0.2;console.log(d.toString(2)); //0.0100110011001100110011001100110011001100110011001101console.log(d.toString(2).length); // 54

浮點數精度運算解決方案

關於 js 浮點數運算精度丟失的問題,不同場景可以有不同的解決方案。 1、如果只是用來展示一個浮點數的結果,則可以借用 Number 對象的 toFixed 和 parseFloat 方法。下麵代碼片段中,fixed 參數表示要保留幾位小數,可以根據實際場景調整精度。

function formatNum(num, fixed = 10) {    return parseFloat(a.toFixed(fixed))}var a = 0.1 + 0.2;console.log(formatNum(a)); //0.3

2、如果需要進行浮點數的加減乘除等運算,由上文可知,在小於 Number.MAXSAFEINTEGER 範圍的整數是可以被精確表示出來的,所以可以先把小數轉化為整數,運算得到結果後再轉化為對應的小數。比如兩個浮點數的加法:

 function add(num1, num2) {  var decimalLen1 = (num1.toString().split('.')[1] || '').length; //第一個參數的小數個數  var decimalLen2 = (num2.toString().split('.')[1] || '').length; //第二個參數的小數個數  var baseNum = Math.pow(10, Math.max(decimalLen1, decimalLen2));  return (num1 * baseNum + num2 * baseNum) / baseNum;}console.log(add(0.1 , 0.2)); //0.3

參考資料

此文已由作者授權騰訊雲+社區發佈



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文由雲+社區發表 絕大多數程式只考慮了介面正常工作的場景,而用戶在使用我們的產品時遇到的各類異常,全都丟在看似 ok 的 try catch 中。如果沒有做好異常的相容和兜底處理,會極大的影響用戶體驗,嚴重的還會帶來安全和資損風險。 介面異常,通常可以分為以下三類: CGI 邏輯出錯。 如調用方入 ...
  • 力軟web快速開發平臺是一款全開源的軟體平臺,為客戶提供集管理咨詢、軟體開發、系統維護為一體的綜合性服務。力軟web快速開發平臺使用了當前最主流開發技術,使web應用的開發更快捷和簡單。 作為一款高效的web開發工具,力軟web包含了眾多的功能組件。 工作流引擎、自定義表單、自定義報表、代碼生成器、 ...
  • js keyup、keypress和keydown事件都是有關於鍵盤的事件 當一個按鍵被pressed 或released在每一個現代瀏覽器中,都可能有三種客戶端事件。 keydown事件發生在鍵盤的鍵被按下的時候,接下來觸發keypress事件。 keyup 事件在按鍵被釋放的時候觸發。 這三個事 ...
  • 顧名思義 用箭頭 “ => ” 定義函數,如果箭頭函數的代碼塊部分有多條語句,就要使用大括弧將它們括起來,並且使用return返回。由於花括弧{ }被解釋為代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上花括弧,否則會報錯。 ...
  • forEach函數用得平時用得比較多,但是從來沒想到forEach函數還有第二個參數。 這裡是菜鳥教程對forEach函數的詳細說明:forEach的詳細說明。 如上圖,forEach函數有第二個參數 thisValue。 簡單點來說,就是我們可以直接使用第二個參數來指定函數里的this的值,而不需 ...
  • (1)node Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。 Node與javaScript的區別在於,javaScript的頂層對象是window,而node是global 模塊:在node中,文件和模塊是一一對應的,也就是一個文件就是一個模塊;每個模塊 ...
  • 數組的增加方法 1.push()方法向數組中末尾添加一個元素,原數組改變 2.unshit()方法向數組中開始元素添加一個元素,原數組改變 3.splice()方法 ary.splice(n,m,x) 從索引n開始刪除m個元素,把新增的元素X放在索引n的前面,把刪除的元素當成一個新數組返回,原有數組 ...
  • 0、URL格式 protocol :// hostname[:port] / path / [;parameters][?query]#fragment [;parameters]沒見過 這裡就不做相關匹配了 1、代碼及運行結果 'use strict'; { // URL地址匹配格式: proto ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...