摘要:排除直接使用的數(shù)太大或太小超出范圍,出現(xiàn)這種問(wèn)題的情況基本是浮點(diǎn)數(shù)的小數(shù)部分在轉(zhuǎn)成二進(jìn)制時(shí)丟失了精度,所以我們可以將小數(shù)部分也轉(zhuǎn)換成整數(shù)后再計(jì)算。
// 1. 兩數(shù)相加 // 0.1 + 0.2 = 0.30000000000000004 // 0.7 + 0.1 = 0.7999999999999999 // 0.2 + 0.4 = 0.6000000000000001 // 2.22 + 0.1 = 2.3200000000000003 // 2. 兩數(shù)相減 // 1.5 - 1.2 = 0.30000000000000004 // 0.3 - 0.2 = 0.09999999999999998 // 3. 兩數(shù)相乘 // 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 // 4. 不一樣的數(shù)卻相等 // 1000000000000000128 === 1000000000000000129
計(jì)算機(jī)的底層實(shí)現(xiàn)就無(wú)法完全精確表示一個(gè)無(wú)限循環(huán)的數(shù),而且能夠存儲(chǔ)的位數(shù)也是有限制的,所以在計(jì)算過(guò)程中只能舍去多余的部分,得到一個(gè)盡可能接近真實(shí)值的數(shù)字表示,于是造成了這種計(jì)算誤差。
比如在 JavaScript 中計(jì)算0.1 + 0.2時(shí),十進(jìn)制的0.1和0.2都會(huì)被轉(zhuǎn)換成二進(jìn)制,但二進(jìn)制并不能完全精確表示轉(zhuǎn)換結(jié)果,因?yàn)榻Y(jié)果是無(wú)限循環(huán)的。
// 百度進(jìn)制轉(zhuǎn)換工具 0.1 -> 0.0001100110011001... 0.2 -> 0.0011001100110011...
In JavaScript, Number is a numeric data type in the double-precision 64-bit floating point format (IEEE 754). In other programming languages different numeric types can exist, for examples: Integers, Floats, Doubles, or Bignums.
根據(jù) MDN這段關(guān)于Number的描述 可以得知,JavaScript 里的數(shù)字是采用 IEEE 754 標(biāo)準(zhǔn)的 64 位雙精度浮點(diǎn)數(shù)。該規(guī)范定義了浮點(diǎn)數(shù)的格式,最大最小范圍,以及超過(guò)范圍的舍入方式等規(guī)范。所以只要不超過(guò)這個(gè)范圍,就不會(huì)存在舍去,也就不會(huì)存在精度問(wèn)題了。比如:
// Number.MAX_SAFE_INTEGER 是 JavaScript 里能表示的最大的數(shù)了,超出了這個(gè)范圍就不能保證計(jì)算的準(zhǔn)確性了 var num = Number.MAX_SAFE_INTEGER; num + 1 === num +2 // = true
實(shí)際工作中我們也用不到這么大的數(shù)或者是很小的數(shù),也應(yīng)該盡量把這種對(duì)精度要求高的計(jì)算交給后端去計(jì)算,因?yàn)楹蠖擞谐墒斓膸?kù)來(lái)解決這個(gè)計(jì)算問(wèn)題。前端雖然也有類似的庫(kù),但是前端引入一個(gè)這樣的庫(kù)代價(jià)太大了。
mathjs
decimal.js
排除直接使用的數(shù)太大或太小超出范圍,出現(xiàn)這種問(wèn)題的情況基本是浮點(diǎn)數(shù)的小數(shù)部分在轉(zhuǎn)成二進(jìn)制時(shí)丟失了精度,所以我們可以將小數(shù)部分也轉(zhuǎn)換成整數(shù)后再計(jì)算。網(wǎng)上很多帖子貼出的解決方案就是這種:
var num1 = 0.1 var num2 = 0.2 (num1 * 10 + num2 * 10) / 10 // = 0.3
但是這樣轉(zhuǎn)換整數(shù)的方式也是一種浮點(diǎn)數(shù)計(jì)算,在轉(zhuǎn)換的過(guò)程中就可能存在精度問(wèn)題,比如:
1306377.64 * 10 // = 13063776.399999999 1306377.64 * 100 // = 130637763.99999999
var num1 = 2.22; var num2 = 0.1; (num1 * 10 + num2 * 10) / 10 // = 2.3200000000000003
所以不要直接通過(guò)計(jì)算將小數(shù)轉(zhuǎn)換成整數(shù),我們可以通過(guò)字符串操作,移動(dòng)小數(shù)點(diǎn)的位置來(lái)轉(zhuǎn)換成整數(shù),最后再同樣通過(guò)字符串操作轉(zhuǎn)換回小數(shù):
/** * 通過(guò)字符串操作將一個(gè)數(shù)放大或縮小指定倍數(shù) * @num 被轉(zhuǎn)換的數(shù) * @m 放大或縮小的倍數(shù),為正表示小數(shù)點(diǎn)向右移動(dòng),表示放大;為負(fù)反之 */ function numScale(num, m) { // 拆分整數(shù)、小數(shù)部分 var parts = num.toString().split("."); // 原始值的整數(shù)位數(shù) const integerLen = parts[0].length; // 原始值的小數(shù)位數(shù) const decimalLen = parts[1] ? parts[1].length : 0; // 放大,當(dāng)放大的倍數(shù)比原來(lái)的小數(shù)位大時(shí),需要在數(shù)字后面補(bǔ)零 if (m > 0) { // 補(bǔ)多少個(gè)零:m - 原始值的小數(shù)位數(shù) let zeros = m - decimalLen; while (zeros > 0) { zeros -= 1; parts.push(0); } // 縮小,當(dāng)縮小的倍數(shù)比原來(lái)的整數(shù)位大時(shí),需要在數(shù)字前面補(bǔ)零 } else { // 補(bǔ)多少個(gè)零:m - 原始值的整數(shù)位數(shù) let zeros = Math.abs(m) - integerLen; while (zeros > 0) { zeros -= 1; parts.unshift(0); } } // 小數(shù)點(diǎn)位置,也是整數(shù)的位數(shù): // 放大:原始值的整數(shù)位數(shù) + 放大的倍數(shù) // 縮?。涸贾档恼麛?shù)位數(shù) - 縮小的倍數(shù) var index = integerLen + m; // 將每一位都拆到數(shù)組里,方便插入小數(shù)點(diǎn) parts = parts.join("").split(""); // 當(dāng)為縮小時(shí),因?yàn)榭赡軙?huì)補(bǔ)零,所以使用原始值的整數(shù)位數(shù) // 計(jì)算出的小數(shù)點(diǎn)位置可能為負(fù),這個(gè)負(fù)數(shù)應(yīng)該正好是補(bǔ)零的 // 個(gè)數(shù),所以小數(shù)點(diǎn)位置應(yīng)該為 0 parts.splice(index > 0 ? index : 0, 0, "."); return parseFloat(parts.join("")); }
/** * 獲取小數(shù)位數(shù) */ function getExponent(num) { return Math.floor(num) === num ? 0 : num.toString().split(".")[1].length; } /** * 兩數(shù)相加 */ function accAdd(num1, num2) { const multiple = Math.max(getExponent(num1), getExponent(num2)); return numScale(numScale(num1, multiple) + numScale(num2, multiple), multiple * -1); }
測(cè)試用例:
describe("accAdd", function() { it("(0.1, 0.2) = 0.3", function() { assert.strictEqual(0.3, _.accAdd(0.1, 0.2)) }) it("(2.22, 0.1) = 2.32", function() { assert.strictEqual(2.32, _.accAdd(2.22, 0.1)) }) it("(11, 11) = 22", function() { assert.strictEqual(22, _.accAdd(11, 11)) }) })
代碼地址:https://github.com/xiaoyann/b...
https://developer.mozilla.org...
JavaScript 中小數(shù)和大整數(shù)的精度丟失
JavaScript 中的數(shù)字
計(jì)算機(jī)是怎么存儲(chǔ)數(shù)字的
為什么0.1無(wú)法被二進(jìn)制小數(shù)精確表示?
為什么0.1+0.2=0.30000000000000004而1.1+2.2=3.3000000000000003?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91186.html
摘要:就像一些無(wú)理數(shù)不能有限表示,如圓周率,等。遵循規(guī)范,采用雙精度存儲(chǔ),占用。參考中不會(huì)失去精度的最大值數(shù)字精度丟失的一些典型問(wèn)題 問(wèn)題描述 后端返回 { spaceObject: { objectId: 1049564069045993472 } } 前端模版,使用的是 atpl 模版 前端獲取 objectId 的方式,const objectId = $(#test).da...
摘要:推導(dǎo)為何等于在中所有數(shù)值都以標(biāo)準(zhǔn)的雙精度浮點(diǎn)數(shù)進(jìn)行存儲(chǔ)的。先來(lái)了解下標(biāo)準(zhǔn)下的雙精度浮點(diǎn)數(shù)。精度位總共是,因?yàn)橛每茖W(xué)計(jì)數(shù)法表示,所以首位固定的就沒(méi)有占用空間。驗(yàn)證完成的最大安全數(shù)是如何來(lái)的根據(jù)雙精度浮點(diǎn)數(shù)的構(gòu)成,精度位數(shù)是。 閱讀完本文可以了解到 0.1 + 0.2 為什么等于 0.30000000000000004 以及 JavaScript 中最大安全數(shù)是如何來(lái)的。 十進(jìn)制小數(shù)轉(zhuǎn)為二...
摘要:基于這個(gè)問(wèn)題運(yùn)動(dòng)基礎(chǔ)問(wèn)題,我想應(yīng)該也有一部分人沒(méi)有認(rèn)真對(duì)待過(guò)中浮點(diǎn)數(shù)的四則運(yùn)算出現(xiàn)的問(wèn)題。解決方案引自解決方案為了解決浮點(diǎn)數(shù)運(yùn)算不準(zhǔn)確的問(wèn)題,在運(yùn)算前我們把參加運(yùn)算的數(shù)先升級(jí)的的次方到整數(shù),等運(yùn)算完后再降級(jí)的的次方。 基于這個(gè)問(wèn)題:javascript運(yùn)動(dòng)基礎(chǔ)問(wèn)題 ,我想應(yīng)該也有一部分人沒(méi)有認(rèn)真對(duì)待過(guò)js中浮點(diǎn)數(shù)的四則運(yùn)算出現(xiàn)的問(wèn)題。 1.問(wèn)題描述 示例代碼: var x ...
摘要:前言最近,朋友問(wèn)了我這樣一個(gè)問(wèn)題在中的運(yùn)算結(jié)果,為什么是這樣的雖然我告訴他說(shuō),這是由于浮點(diǎn)數(shù)精度問(wèn)題導(dǎo)致的。由于可以用階碼移動(dòng)小數(shù)點(diǎn),因此稱為浮點(diǎn)數(shù)。它的實(shí)現(xiàn)遵循標(biāo)準(zhǔn),使用位精度來(lái)表示浮點(diǎn)數(shù)。 showImg(https://segmentfault.com/img/remote/1460000018981071); 前言 最近,朋友 L 問(wèn)了我這樣一個(gè)問(wèn)題:在 chrome 中的運(yùn)算...
摘要:前言在數(shù)據(jù)敏感的業(yè)務(wù)場(chǎng)景中,常常會(huì)碰到數(shù)據(jù)精度問(wèn)題,尤其在金額顯示占比統(tǒng)計(jì)等地方,該問(wèn)題尤為顯著。計(jì)算機(jī)原理真香數(shù)值的精度問(wèn)題,其實(shí)是非?;A(chǔ)的計(jì)算機(jī)原理知識(shí)。前言 在數(shù)據(jù)敏感的業(yè)務(wù)場(chǎng)景中,常常會(huì)碰到數(shù)據(jù)精度問(wèn)題,尤其在金額顯示、占比統(tǒng)計(jì)等地方,該問(wèn)題尤為顯著。由于數(shù)據(jù)的每一位有效數(shù)字都包含真實(shí)的業(yè)務(wù)語(yǔ)義,一點(diǎn)點(diǎn)偏差甚至可能影響業(yè)務(wù)決策,這讓問(wèn)題的嚴(yán)重性上升了幾個(gè)階梯。 那,什么是精度丟失...
閱讀 2590·2021-11-18 10:02
閱讀 1720·2021-09-30 10:00
閱讀 5351·2021-09-22 15:27
閱讀 1224·2019-08-30 15:54
閱讀 3685·2019-08-29 11:13
閱讀 2959·2019-08-29 11:05
閱讀 3336·2019-08-29 11:01
閱讀 581·2019-08-26 13:52