欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JavaScript 浮點數(shù)運算的精度問題

 更新時間:2019年07月23日 09:45:44   投稿:zx  
這篇文章主要介紹了詳解JavaScript 浮點數(shù)運算的精度問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

問題描述

在 JavaScript 中整數(shù)和浮點數(shù)都屬于 Number 數(shù)據(jù)類型,所有數(shù)字都是以 64 位浮點數(shù)形式儲存,即便整數(shù)也是如此。 所以我們在打印 1.00 這樣的浮點數(shù)的結果是 1 而非 1.00 。在一些特殊的數(shù)值表示中,例如金額,這樣看上去有點變扭,但是至少值是正確了。然而要命的是,當浮點數(shù)做數(shù)學運算的時候,你經(jīng)常會發(fā)現(xiàn)一些問題,舉幾個例子:

// 加法 =====================
// 0.1 + 0.2 = 0.30000000000000004
// 0.7 + 0.1 = 0.7999999999999999
// 0.2 + 0.4 = 0.6000000000000001
// 2.22 + 0.1 = 2.3200000000000003
 
// 減法 =====================
// 1.5 - 1.2 = 0.30000000000000004
// 0.3 - 0.2 = 0.09999999999999998
 
// 乘法 =====================
// 19.9 * 100 = 1989.9999999999998
// 19.9 * 10 * 10 = 1990
// 1306377.64 * 100 = 130637763.99999999
// 1306377.64 * 10 * 10 = 130637763.99999999
// 0.7 * 180 = 125.99999999999999
// 9.7 * 100 = 969.9999999999999
// 39.7 * 100 = 3970.0000000000005
 
// 除法 =====================
// 0.3 / 0.1 = 2.9999999999999996
// 0.69 / 10 = 0.06899999999999999

問題的原因

似乎是不可思議。小學生都會算的題目,JavaScript 不會?我們來看看其真正的原因。

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

  1. 第0位:符號位, s 表示 ,0表示正數(shù),1表示負數(shù);
  2. 第1位到第11位:儲存指數(shù)部分, e 表示 ;
  3. 第12位到第63位:儲存小數(shù)部分(即有效數(shù)字),f 表示,

如圖:

符號位決定了一個數(shù)的正負,指數(shù)部分決定了數(shù)值的大小,小數(shù)部分決定了數(shù)值的精度。 IEEE 754規(guī)定,有效數(shù)字第一位默認總是1,不保存在64位浮點數(shù)之中。也就是說,有效數(shù)字總是1.xx…xx的形式,其中xx..xx的部分保存在64位浮點數(shù)之中,最長可能為52位。因此,JavaScript提供的有效數(shù)字最長為53個二進制位(64位浮點的后52位+有效數(shù)字第一位的1)。

計算過程

比如在 JavaScript 中計算 0.1 + 0.2時,到底發(fā)生了什么呢?

首先,十進制的0.1和0.2都會被轉換成二進制,但由于浮點數(shù)用二進制表達時是無窮的,例如。

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

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

0.0100110011001100110011001100110011001100110011001100 

因浮點數(shù)小數(shù)位的限制而截斷的二進制數(shù)字,再轉換為十進制,就成了 0.30000000000000004。所以在進行算術計算時會產(chǎn)生誤差。

整數(shù)的精度問題

在 Javascript 中,整數(shù)精度同樣存在問題,先來看看問題:

console.log(19571992547450991); //=> 19571992547450990
console.log(19571992547450991===19571992547450992); //=> true

同樣的原因,在 JavaScript 中 Number類型統(tǒng)一按浮點數(shù)處理,整數(shù)是按最大54位來算最大(253 - 1,Number.MAX_SAFE_INTEGER,9007199254740991) 和最小(-(253 - 1),Number.MIN_SAFE_INTEGER,-9007199254740991) 安全整數(shù)范圍的。所以只要超過這個范圍,就會存在被舍去的精度問題。

當然這個問題并不只是在 Javascript 中才會出現(xiàn),幾乎所有的編程語言都采用了 IEEE-745 浮點數(shù)表示法,任何使用二進制浮點數(shù)的編程語言都會有這個問題,只不過在很多其他語言中已經(jīng)封裝好了方法來避免精度的問題,而 JavaScript 是一門弱類型的語言,從設計思想上就沒有對浮點數(shù)有個嚴格的數(shù)據(jù)類型,所以精度誤差的問題就顯得格外突出。

解決方案

上面說了這么多問題和原因,這里給出一些解決方案。

類庫

通常這種對精度要求高的計算都應該交給后端去計算和存儲,因為后端有成熟的庫來解決這種計算問題。前端也有幾個不錯的類庫:

Math.js

Math.js 是專門為 JavaScript 和 Node.js 提供的一個廣泛的數(shù)學庫。它具有靈活的表達式解析器,支持符號計算,配有大量內(nèi)置函數(shù)和常量,并提供集成解決方案來處理不同的數(shù)據(jù)類型

像數(shù)字,大數(shù)字(超出安全數(shù)的數(shù)字),復數(shù),分數(shù),單位和矩陣。 功能強大,易于使用。

官網(wǎng):http://mathjs.org/

GitHub:https://github.com/josdejong/mathjs

decimal.js

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

官網(wǎng):http://mikemcl.github.io/decimal.js/

GitHub:https://github.com/MikeMcl/decimal.js

big.js

官網(wǎng):http://mikemcl.github.io/big.js

GitHub:https://github.com/MikeMcl/big.js/

這幾個類庫幫我們解決很多這類問題,不過通常我們前端做這類運算通常只用于表現(xiàn)層,應用并不是很多。所以很多時候,一個函數(shù)能解決的問題不需要引用一個類庫來解決。

下面介紹各個更加簡單的解決方案。

整數(shù)表示

對于整數(shù),我們可以通過用String類型的表示來取值或傳值,否則會喪失精度。

格式化數(shù)字、金額、保留幾位小數(shù)等

如果只是格式化數(shù)字、金額、保留幾位小數(shù)等可以查看這里 http://www.dbjr.com.cn/article/165993.htm

浮點數(shù)運算

toFixed() 方法

浮點數(shù)運算的解決方案有很多,這里給出一種目前常用的解決方案, 在判斷浮點數(shù)運算結果前對計算結果進行精度縮小,因為在精度縮小的過程總會自動四舍五入。

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

numObj.toFixed(digits)

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

返回一個數(shù)值的字符串表現(xiàn)形式,不使用指數(shù)記數(shù)法,而是在小數(shù)點后有 digits 位數(shù)字。該數(shù)值在必要時進行四舍五入,另外在必要時會用 0 來填充小數(shù)部分,以便小數(shù)部分有指定的位數(shù)。 如果數(shù)值大于 1e+21,該方法會簡單調(diào)用 Number.prototype.toString()并返回一個指數(shù)記數(shù)法格式的字符串。

特別注意:toFixed() 返回一個數(shù)值的字符串表現(xiàn)形式。

具體可以查看MDN中的說明,那么我們可以這樣解決精度問題:

parseFloat((數(shù)學表達式).toFixed(digits)); // toFixed() 精度參數(shù)須在 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)) // 結果為 970 
parseFloat((2.22 + 0.1).toFixed(10)) // 結果為 2.32

在老版本的IE瀏覽器(IE 6,7,8)中,toFixed()方法返回值不一定準確。所以這個方法以前很少用。以至于網(wǎng)上搜索出來的結果大都是下面這些方法。

還有一些其他的解決方案,簡單的說需要將浮點數(shù)轉換字符串,分隔成為整數(shù)部分和小數(shù)部分,小數(shù)部分再轉換為整數(shù),計算結果后,再轉換為浮點數(shù)。這過程有點復雜…,網(wǎng)上找一下:

加法函數(shù)

/**
 ** 加法函數(shù),用來得到精確的加法結果
 ** 說明:javascript的加法結果會有誤差,在兩個浮點數(shù)相加的時候會比較明顯。這個函數(shù)返回較為精確的加法結果。
 ** 調(diào)用:accAdd(arg1,arg2)
 ** 返回值:arg1加上arg2的精確結果
 **/
function accAdd(arg1, arg2) {
 var r1, r2, m, c;
 try {
  r1 = arg1.toString().split(".")[1].length;
 }
 catch (e) {
  r1 = 0;
 }
 try {
  r2 = arg2.toString().split(".")[1].length;
 }
 catch (e) {
  r2 = 0;
 }
 c = Math.abs(r1 - r2);
 m = Math.pow(10, Math.max(r1, r2));
 if (c > 0) {
  var cm = Math.pow(10, c);
  if (r1 > r2) {
   arg1 = Number(arg1.toString().replace(".", ""));
   arg2 = Number(arg2.toString().replace(".", "")) * cm;
  } else {
   arg1 = Number(arg1.toString().replace(".", "")) * cm;
   arg2 = Number(arg2.toString().replace(".", ""));
  }
 } else {
  arg1 = Number(arg1.toString().replace(".", ""));
  arg2 = Number(arg2.toString().replace(".", ""));
 }
 return (arg1 + arg2) / m;
}
 
//給Number類型增加一個add方法,調(diào)用起來更加方便。
Number.prototype.add = function (arg) {
 return accAdd(arg, this);
};

減法函數(shù)

/**
 ** 減法函數(shù),用來得到精確的減法結果
 ** 說明:javascript的減法結果會有誤差,在兩個浮點數(shù)相減的時候會比較明顯。這個函數(shù)返回較為精確的減法結果。
 ** 調(diào)用:accSub(arg1,arg2)
 ** 返回值:arg1加上arg2的精確結果
 **/
function accSub(arg1, arg2) {
 var r1, r2, m, n;
 try {
  r1 = arg1.toString().split(".")[1].length;
 }
 catch (e) {
  r1 = 0;
 }
 try {
  r2 = arg2.toString().split(".")[1].length;
 }
 catch (e) {
  r2 = 0;
 }
 m = Math.pow(10, Math.max(r1, r2)); //last modify by deeka //動態(tài)控制精度長度
 n = (r1 >= r2) ? r1 : r2;
 return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
 
// 給Number類型增加一個mul方法,調(diào)用起來更加方便。
Number.prototype.sub = function (arg) {
 return accMul(arg, this);
};

乘法函數(shù)

/**
 ** 乘法函數(shù),用來得到精確的乘法結果
 ** 說明:javascript的乘法結果會有誤差,在兩個浮點數(shù)相乘的時候會比較明顯。這個函數(shù)返回較為精確的乘法結果。
 ** 調(diào)用:accMul(arg1,arg2)
 ** 返回值:arg1乘以 arg2的精確結果
 **/
function accMul(arg1, arg2) {
 var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
 try {
  m += s1.split(".")[1].length;
 }
 catch (e) {
 }
 try {
  m += s2.split(".")[1].length;
 }
 catch (e) {
 }
 return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m);
}
 
// 給Number類型增加一個mul方法,調(diào)用起來更加方便。
Number.prototype.mul = function (arg) {
 return accMul(arg, this);
};

除法函數(shù)

/** 
 ** 除法函數(shù),用來得到精確的除法結果
 ** 說明:javascript的除法結果會有誤差,在兩個浮點數(shù)相除的時候會比較明顯。這個函數(shù)返回較為精確的除法結果。
 ** 調(diào)用:accDiv(arg1,arg2)
 ** 返回值:arg1除以arg2的精確結果
 **/
function accDiv(arg1, arg2) {
 var t1 = 0, t2 = 0, r1, r2;
 try {
  t1 = arg1.toString().split(".")[1].length;
 }
 catch (e) {
 }
 try {
  t2 = arg2.toString().split(".")[1].length;
 }
 catch (e) {
 }
 with (Math) {
  r1 = Number(arg1.toString().replace(".", ""));
  r2 = Number(arg2.toString().replace(".", ""));
  return (r1 / r2) * pow(10, t2 - t1);
 }
}
 
//給Number類型增加一個div方法,調(diào)用起來更加方便。
Number.prototype.div = function (arg) {
 return accDiv(this, arg);
};

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • JavaScript function函數(shù)種類詳解

    JavaScript function函數(shù)種類詳解

    這篇文章主要為大家詳細介紹了JavaScript function函數(shù)種類,包括普通函數(shù)、匿名函數(shù)、閉包函數(shù),感興趣的小伙伴們可以參考一下
    2016-02-02
  • 讓JavaScript代碼更加精簡的方法技巧

    讓JavaScript代碼更加精簡的方法技巧

    這篇文章主要介紹了讓JavaScript代碼更加精簡的方法技巧,文章通過使用?JavaScript對象解構來節(jié)省代碼,JavaScript?對象解構賦值在項目開發(fā)中是一個常用的技能。下文實現(xiàn)過程需要的小伙伴可以參考一下
    2022-06-06
  • BootStrap學習系列之布局組件(下拉,按鈕組[toolbar],上拉)

    BootStrap學習系列之布局組件(下拉,按鈕組[toolbar],上拉)

    這篇文章主要介紹了BootStrap學習系列之布局組件(下拉,按鈕組[toolbar],上拉)的相關資料,需要的朋友可以參考下
    2017-01-01
  • JavaScript中數(shù)組去重的辦法總結

    JavaScript中數(shù)組去重的辦法總結

    你是否在面試的過程中被考到過給你一個數(shù)組讓你去掉重復項呢,下面小編就來總結一下對于數(shù)組去重這道簡單的面試題時,我們可以回答的方法有什么吧
    2023-06-06
  • array.prototype.silce.call 理解分析

    array.prototype.silce.call 理解分析

    在很多框架中與遇到這個方法,但一時沒去研究這個方法,只要前段時間被問到,尷尬無奈收場。所以學習就要追根究底的精神,廢話少說,說正題。
    2010-04-04
  • 關于JavaScript數(shù)組你所不知道的3件事

    關于JavaScript數(shù)組你所不知道的3件事

    這篇文章主要為大家詳細介紹了關于JavaScript數(shù)組三個并不那么常見的功能,你所不知道的事情,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JavaScript建立一個語法高亮輸入框?qū)崿F(xiàn)思路

    JavaScript建立一個語法高亮輸入框?qū)崿F(xiàn)思路

    通常網(wǎng)站自帶的textarea編輯器不能滿足我們的需求比如高亮顯示代碼等,在這篇文章中,我將使用JavaScript庫ACE來創(chuàng)建一個輸入框效果,該腳本允許開發(fā)人員創(chuàng)建支持語法高亮的輸入框,感興趣的你可不要錯過了哈
    2013-02-02
  • PHP中使用微秒計算腳本執(zhí)行時間例子

    PHP中使用微秒計算腳本執(zhí)行時間例子

    這篇文章主要介紹了PHP中使用微秒計算腳本執(zhí)行時間例子,本文先是講解了microtime函數(shù)的一些知識,然后給出了一個計算腳本運行時間的類,需要的朋友可以參考下
    2014-11-11
  • js通過googleAIP翻譯PHP系統(tǒng)的語言配置的實現(xiàn)代碼

    js通過googleAIP翻譯PHP系統(tǒng)的語言配置的實現(xiàn)代碼

    一同事弄了個系統(tǒng)是php寫的,雖然是多語言但沒中文!他打算手動翻譯2000多個語言配置,真是佩服,知道后想了想,應該有好的法辦
    2011-10-10
  • Javascript中引用類型傳遞的知識點小結

    Javascript中引用類型傳遞的知識點小結

    這篇文章主要給大家介紹了關于Javascript中引用類型傳遞的知識點,文中介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-03-03

最新評論