日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

javascript进制转换_JavaScript 加减危机——为什么会出现这样的结果?

發布時間:2025/3/11 javascript 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript进制转换_JavaScript 加减危机——为什么会出现这样的结果? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在日常工作計算中,我們如履薄冰,但是 JavaScript 總能給我們這樣那樣的 surprise~

  • 0.1 + 0.2 = ?
  • 1 - 0.9 = ?
  • 如果小伙伴給出內心的結果:

  • 0.1 + 0.2 = 0.3
  • 1 - 0.9 = 0.1
  • 那么小伙伴會被事實狠狠地扇臉:

    console.log(0.1 + 0.2); // 0.30000000000000004console.log(1 - 0.9); // 0.09999999999999998

    為什么會出現這種情況呢?咱們一探究竟!

    三 問題復現

    返回目錄

    下面,我們會通過探討 IEEE 754 標準,以及 JavaScript 加減的計算過程,來復現問題。

    3.1 根源:IEEE 754 標準

    返回目錄

    JavaScript 里面的數字采用 IEEE 754 標準的 64 位雙精度浮點數。該規范定義了浮點數的格式,對于 64 位的浮點數在內存中表示,最高的 1 位是符號為,接著的 11 位是指數,剩下的 52 位為有效數字,具體:

    • 第 0 位:符號位。用 s 表示,0 表示為正數,1 表示為負數;
    • 第 1 - 11 位:存儲指數部分。用 e 表示;
    • 第 12 - 63 位:存儲小數部分(即有效數字)。用 f 表示。

    符號位決定一個數的正負,指數部分決定數值的大小,小數部分決定數值的精度。

    IEEE 754 規定,有效數字第一位默認總是 1,不保存在 64 位浮點數之中。

    也就是說,有效數字總是 1.XX......XX的形式,其中 XX......XX 的部分保存在 64 位浮點數之中,最長可能為 52 位。

    因此,JavaScript 提供的有效數字最長為 53 個二進制位(64 位浮點的后 52 位 + 有效數字第一位的 1)。

    3.2 復現:計算過程

    返回目錄

    通過 JavaScript 計算 0.1 + 0.2 時,會發生什么?

    1、 將 0.1 和 0.2 換成二進制表示:

    0.1 -> 0.0001100110011001...(無限)0.2 -> 0.0011001100110011...(無限

    浮點數用二進制表達式是無窮的

    我自己是一名從事了多年開發的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年年初我花了一個月整理了一份最適合2019年學習的web前端學習干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:前端,即可免費獲取。

    2、 因為 IEEE 754 標準的 64 位雙精度浮點數的小數部分最多支持 53 位二進制位,所以兩者相加之后得到二進制為:

    0.0100110011001100110011001100110011001100110011001100

    因為浮點數小數位的限制,這個二進制數字被截斷了,用這個二進制數轉換成十進制,就成了 0.30000000000000004,從而在進行算數計算時產生誤差。

    3.3 擴展:數字安全

    返回目錄

    在看完上面小數的計算不精確后,jsliang 覺得有必要再聊聊整數,因為整數同樣存在一些問題:

    console.log(19571992547450991);// 19571992547450990console.log(19571992547450991 === 19571992547450994);// true

    是不是很驚奇!

    因為 JavaScript 中 Number 類型統一按浮點數處理,整數也不能逃避這個問題:

    // 最大值const MaxNumber = Math.pow(2, 53) - 1;console.log(MaxNumber); // 9007199254740991console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991// 最小值const MinNumber = -(Math.pow(2, 53) - 1);console.log(MinNumber); // -9007199254740991console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

    即整數的安全范圍是: [-9007199254740991, 9007199254740991]。

    超過這個范圍的,就存在被舍去的精度問題。

    當然,這個問題并不僅僅存在于 JavaScript 中,幾乎所有采用了 IEEE-745 標準的編程語言,都會有這個問題,只不過在很多其他語言中已經封裝好了方法來避免精度的問題。

    • PHP Float 浮點型 - Manual
    • Java 您的小數點到哪里去了? - Brian Goetz

    而因為 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數有個嚴格的數據類型,所以精度誤差的問題就顯得格外突出。

    到此為止,我們可以看到 JavaScript 在處理數字類型的操作時,可能會產生一些問題。

    事實上,工作中還真會有問題!

    某天我處理了一個工作表格的計算,然后第二天被告知線上有問題,之后被產品小姐姐問話:

    • 為什么小學生都能做出的小數計算,你們計算機算不了呢?

    默哀三秒,產生上面的找到探索,最終找到下面的解決方案。

    四 解決問題

    返回目錄

    下面嘗試通過各種方式來解決浮點數計算的問題。

    4.1 toFixed()

    返回目錄

    toFixed() 方法使用定點表示法來格式化一個數值。

    • 《toFixed - MDN》

    語法:numObj.toFixed(digits)

    參數:digits。小數點后數字的個數;介于 0 到 20(包括)之間,實現環境可能支持更大范圍。如果忽略該參數,則默認為 0。

    const num = 12345.6789;num.toFixed(); // '12346':進行四舍五入,不包括小數部分。num.toFixed(1); // '12345.7':進行四舍五入,保留小數點后 1 個數字。num.toFixed(6); // '12345.678900':保留小數點后 6 個數字,長度不足時用 0 填充。(1.23e+20).toFixed(2); // 123000000000000000000.00 科學計數法變成正常數字類型

    toFixed() 得出的結果是 String 類型,記得轉換 Number 類型。

    toFixed() 方法使用定點表示法來格式化一個數,會對結果進行四舍五入。

    通過 toFixed() 我們可以解決一些問題:

    原加減乘數:

    console.log(1.0 - 0.9);// 0.09999999999999998console.log(0.3 / 0.1);// 2.9999999999999996console.log(9.7 * 100);// 969.9999999999999console.log(2.22 + 0.1);// 2.3200000000000003

    使用 toFixed():

    // 公式:parseFloat((數學表達式).toFixed(digits));// toFixed() 精度參數須在 0 與20 之間parseFloat((1.0 - 0.9).toFixed(10));// 0.1 parseFloat((0.3 / 0.1).toFixed(10));// 3 parseFloat((9.7 * 100).toFixed(10));// 970parseFloat((2.22 + 0.1).toFixed(10));// 2.32

    那么,講到這里,問題來了:

    • parseFloat(1.005.toFixed(2))

    會得到什么呢,你的反應是不是 1.01 ?

    然而并不是,結果是:1。

    這么說的話,enm...摔!o(╥﹏╥)o

    toFixed() 被證明了也不是最保險的解決方式。

    4.2 手寫簡易加減乘除

    返回目錄

    既然 JavaScript 自帶的方法不能自救,那么我們只能換個思路:

    • 將 JavaScript 的小數部分轉成字符串進行計算
    /** * @name 檢測數據是否超限 * @param {Number} number */const checkSafeNumber = (number) => { if (number > Number.MAX_SAFE_INTEGER || number < Number.MIN_SAFE_INTEGER) { console.log(`數字 ${number} 超限,請注意風險!`); }};/** * @name 修正數據 * @param {Number} number 需要修正的數字 * @param {Number} precision 端正的位數 */const revise = (number, precision = 12) => { return +parseFloat(number.toPrecision(precision));}/** * @name 獲取小數點后面的長度 * @param {Number} 需要轉換的數字 */const digitLength = (number) => { return (number.toString().split('.')[1] || '').length;};/** * @name 將數字的小數點去掉 * @param {Number} 需要轉換的數字 */const floatToInt = (number) => { return Number(number.toString().replace('.', ''));};/** * @name 精度計算乘法 * @param {Number} arg1 乘數 1 * @param {Number} arg2 乘數 2 */const multiplication = (arg1, arg2) => { const baseNum = digitLength(arg1) + digitLength(arg2); const result = floatToInt(arg1) * floatToInt(arg2); checkSafeNumber(result); return result / Math.pow(10, baseNum); // 整數安全范圍內的兩個整數進行除法是沒問題的 // 如果有,證明給我看};console.log('------乘法:');console.log(9.7 * 100); // 969.9999999999999console.log(multiplication(9.7, 100)); // 970console.log(0.01 * 0.07); // 0.0007000000000000001console.log(multiplication(0.01, 0.07)); // 0.0007console.log(1207.41 * 100); // 120741.00000000001console.log(multiplication(1207.41, 100)); // 0.0007/** * @name 精度計算加法 * @description JavaScript 的加法結果存在誤差,兩個浮點數 0.1 + 0.2 !== 0.3,使用這方法能去除誤差。 * @param {Number} arg1 加數 1 * @param {Number} arg2 加數 2 * @return arg1 + arg2 */const add = (arg1, arg2) => { const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2))); return (multiplication(arg1, baseNum) + multiplication(arg2, baseNum)) / baseNum;}console.log('------加法:');console.log(1.001 + 0.003); // 1.0039999999999998console.log(add(1.001, 0.003)); // 1.004console.log(3.001 + 0.07); // 3.0709999999999997console.log(add(3.001, 0.07)); // 3.071/** * @name 精度計算減法 * @param {Number} arg1 減數 1 * @param {Number} arg2 減數 2 */const subtraction = (arg1, arg2) => { const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2))); return (multiplication(arg1, baseNum) - multiplication(arg2, baseNum)) / baseNum;};console.log('------減法:');console.log(0.3 - 0.1); // 0.19999999999999998console.log(subtraction(0.3, 0.1)); // 0.2/** * @name 精度計算除法 * @param {Number} arg1 除數 1 * @param {Number} arg2 除數 2 */const division = (arg1, arg2) => { const baseNum = Math.pow(10, Math.max(digitLength(arg1), digitLength(arg2))); return multiplication(arg1, baseNum) / multiplication(arg2, baseNum);};console.log('------除法:');console.log(0.3 / 0.1); // 2.9999999999999996console.log(division(0.3, 0.1)); // 3console.log(1.21 / 1.1); // 1.0999999999999999console.log(division(1.21, 1.1)); // 1.1console.log(1.02 / 1.1); // 0.9272727272727272console.log(division(1.02, 1.1)); // 數字 9272727272727272 超限,請注意風險!0.9272727272727272console.log(1207.41 / 100); // 12.074100000000001console.log(division(1207.41, 100)); // 12.0741/** * @name 按指定位數四舍五入 * @param {Number} number 需要取舍的數字 * @param {Number} ratio 精確到多少位小數 */const round = (number, ratio) => { const baseNum = Math.pow(10, ratio); return division(Math.round(multiplication(number, baseNum)), baseNum); // Math.round() 進行小數點后一位四舍五入是否有問題,如果有,請證明出來 // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/round}console.log('------四舍五入:');console.log(0.105.toFixed(2)); // '0.10'console.log(round(0.105, 2)); // 0.11console.log(1.335.toFixed(2)); // '1.33'console.log(round(1.335, 2)); // 1.34console.log(-round(2.5, 0)); // -3console.log(-round(20.51, 0)); // -21

    在這份代碼中,我們先通過石錘乘法的計算,通過將數字轉成整數進行計算,從而產生了 安全 的數據。

    JavaScript 整數運算會不會出問題呢?

    乘法計算好后,假設乘法已經沒問題,然后通過乘法推出 加法、減法 以及 除法 這三則運算。

    最后,通過乘法和除法做出四舍五入的規則。

    JavaScript Math.round() 產生的數字會不會有問題呢、

    這樣,我們就搞定了兩個數的加減乘除和四舍五入(保留指定的長度),那么,里面會不會有問題呢?

    如果有,請例舉出來。

    如果沒有,那么你能不能依據上面兩個數的加減乘除,實現三個數甚至多個數的加減乘除?

    五 現成框架

    返回目錄

    這么重要的計算,如果自己寫的話你總會感覺惶惶不安,感覺充滿著危機。

    所以很多時候,我們可以使用大佬們寫好的 JavaScript 計算庫,因為這些問題大佬已經幫我們進行了大量的測試了,大大減少了我們手寫存在的問題,所以我們可以調用別人寫好的類庫。

    下面推薦幾款不錯的類庫:

    • Math.js。

    Math.js 是一個用于 JavaScript 和 Node.js 的擴展數學庫。

    它具有支持符號計算的靈活表達式解析器,大量內置函數和常量,并提供了集成的解決方案來處理不同的數據類型,例如數字,大數,復數,分數,單位和矩陣。

    強大且易于使用。

    • decimal.js

    JavaScript 的任意精度的十進制類型。

    • big.js

    一個小型,快速,易于使用的庫,用于任意精度的十進制算術運算。

    • bignumber.js

    一個用于任意精度算術的 JavaScript 庫。

    最后的最后,值得一提的是:如果對數字的計算非常嚴格,或許你可以將參數丟給后端,讓后端進行計算,再返回給你結果。

    例如涉及到比特幣、商城商品價格等的計算~


    作者:jsliang
    鏈接:https://juejin.im/post/5ddc7fa66fb9a07ad665b1f0

    總結

    以上是生活随笔為你收集整理的javascript进制转换_JavaScript 加减危机——为什么会出现这样的结果?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。