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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浮点数运算的精度问题

發(fā)布時間:2024/1/1 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浮点数运算的精度问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

問題描述
在 JavaScript 中整數(shù)和浮點數(shù)都屬于 Number 數(shù)據(jù)類型,所有數(shù)字都是以 64 位浮點數(shù)形式儲存,即便整數(shù)也是如此。 所以我們在打印 1.00 這樣的浮點數(shù)的結(jié)果是 1 而非 1.00 。在一些特殊的數(shù)值表示中,例如金額,這樣看上去有點變扭,但是至少值是正確了。然而要命的是,當(dāng)浮點數(shù)做數(shù)學(xué)運算的時候,你經(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

問題的原因
似乎是不可思議。小學(xué)生都會算的題目,JavaScript 不會?我們來看看其真正的原因。
JavaScript 里的數(shù)字是采用 IEEE 754 標(biāo)準(zhǔn)的 64 位雙精度浮點數(shù)。該規(guī)范定義了浮點數(shù)的格式,對于64位的浮點數(shù)在內(nèi)存中的表示,最高的1位是符號位,接著的11位是指數(shù),剩下的52位為有效數(shù)字,具體:
第0位:符號位, s 表示 ,0表示正數(shù),1表示負(fù)數(shù);
第1位到第11位:儲存指數(shù)部分, e 表示 ;
第12位到第63位:儲存小數(shù)部分(即有效數(shù)字),f 表示,
如圖:
計算過程
比如在 JavaScript 中計算 0.1 + 0.2時,到底發(fā)生了什么呢?
首先,十進(jìn)制的0.1和0.2都會被轉(zhuǎn)換成二進(jìn)制,但由于浮點數(shù)用二進(jìn)制表達(dá)時是無窮的,例如。

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

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

0.0100110011001100110011001100110011001100110011001100

因浮點數(shù)小數(shù)位的限制而截斷的二進(jìn)制數(shù)字,再轉(zhuǎn)換為十進(jìn)制,就成了 0.30000000000000004。所以在進(jìn)行算術(shù)計算時會產(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ù)范圍的。所以只要超過這個范圍,就會存在被舍去的精度問題。

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

解決方案
加法函數(shù)

/**** 加法函數(shù),用來得到精確的加法結(jié)果** 說明:javascript的加法結(jié)果會有誤差,在兩個浮點數(shù)相加的時候會比較明顯。這個函數(shù)返回較為精確的加法結(jié)果。** 調(diào)用:accAdd(arg1,arg2)** 返回值:arg1加上arg2的精確結(jié)果**/ 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ù),用來得到精確的減法結(jié)果** 說明:javascript的減法結(jié)果會有誤差,在兩個浮點數(shù)相減的時候會比較明顯。這個函數(shù)返回較為精確的減法結(jié)果。** 調(diào)用:accSub(arg1,arg2)** 返回值:arg1加上arg2的精確結(jié)果**/ 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ù),用來得到精確的乘法結(jié)果** 說明:javascript的乘法結(jié)果會有誤差,在兩個浮點數(shù)相乘的時候會比較明顯。這個函數(shù)返回較為精確的乘法結(jié)果。** 調(diào)用:accMul(arg1,arg2)** 返回值:arg1乘以 arg2的精確結(jié)果**/ 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ù),用來得到精確的除法結(jié)果** 說明:javascript的除法結(jié)果會有誤差,在兩個浮點數(shù)相除的時候會比較明顯。這個函數(shù)返回較為精確的除法結(jié)果。** 調(diào)用:accDiv(arg1,arg2)** 返回值:arg1除以arg2的精確結(jié)果**/ 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); };

總結(jié)

以上是生活随笔為你收集整理的浮点数运算的精度问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。