摘要:排除直接使用的數太大或太小超出范圍,出現這種問題的情況基本是浮點數的小數部分在轉成二進制時丟失了精度,所以我們可以將小數部分也轉換成整數后再計算。
// 1. 兩數相加 // 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. 兩數相減 // 1.5 - 1.2 = 0.30000000000000004 // 0.3 - 0.2 = 0.09999999999999998 // 3. 兩數相乘 // 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. 不一樣的數卻相等 // 1000000000000000128 === 1000000000000000129
計算機的底層實現就無法完全精確表示一個無限循環的數,而且能夠存儲的位數也是有限制的,所以在計算過程中只能舍去多余的部分,得到一個盡可能接近真實值的數字表示,于是造成了這種計算誤差。
比如在 JavaScript 中計算0.1 + 0.2時,十進制的0.1和0.2都會被轉換成二進制,但二進制并不能完全精確表示轉換結果,因為結果是無限循環的。
// 百度進制轉換工具 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.
根據 MDN這段關于Number的描述 可以得知,JavaScript 里的數字是采用 IEEE 754 標準的 64 位雙精度浮點數。該規范定義了浮點數的格式,最大最小范圍,以及超過范圍的舍入方式等規范。所以只要不超過這個范圍,就不會存在舍去,也就不會存在精度問題了。比如:
// Number.MAX_SAFE_INTEGER 是 JavaScript 里能表示的最大的數了,超出了這個范圍就不能保證計算的準確性了 var num = Number.MAX_SAFE_INTEGER; num + 1 === num +2 // = true
實際工作中我們也用不到這么大的數或者是很小的數,也應該盡量把這種對精度要求高的計算交給后端去計算,因為后端有成熟的庫來解決這個計算問題。前端雖然也有類似的庫,但是前端引入一個這樣的庫代價太大了。
mathjs
decimal.js
排除直接使用的數太大或太小超出范圍,出現這種問題的情況基本是浮點數的小數部分在轉成二進制時丟失了精度,所以我們可以將小數部分也轉換成整數后再計算。網上很多帖子貼出的解決方案就是這種:
var num1 = 0.1 var num2 = 0.2 (num1 * 10 + num2 * 10) / 10 // = 0.3
但是這樣轉換整數的方式也是一種浮點數計算,在轉換的過程中就可能存在精度問題,比如:
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
所以不要直接通過計算將小數轉換成整數,我們可以通過字符串操作,移動小數點的位置來轉換成整數,最后再同樣通過字符串操作轉換回小數:
/** * 通過字符串操作將一個數放大或縮小指定倍數 * @num 被轉換的數 * @m 放大或縮小的倍數,為正表示小數點向右移動,表示放大;為負反之 */ function numScale(num, m) { // 拆分整數、小數部分 var parts = num.toString().split("."); // 原始值的整數位數 const integerLen = parts[0].length; // 原始值的小數位數 const decimalLen = parts[1] ? parts[1].length : 0; // 放大,當放大的倍數比原來的小數位大時,需要在數字后面補零 if (m > 0) { // 補多少個零:m - 原始值的小數位數 let zeros = m - decimalLen; while (zeros > 0) { zeros -= 1; parts.push(0); } // 縮小,當縮小的倍數比原來的整數位大時,需要在數字前面補零 } else { // 補多少個零:m - 原始值的整數位數 let zeros = Math.abs(m) - integerLen; while (zeros > 0) { zeros -= 1; parts.unshift(0); } } // 小數點位置,也是整數的位數: // 放大:原始值的整數位數 + 放大的倍數 // 縮小:原始值的整數位數 - 縮小的倍數 var index = integerLen + m; // 將每一位都拆到數組里,方便插入小數點 parts = parts.join("").split(""); // 當為縮小時,因為可能會補零,所以使用原始值的整數位數 // 計算出的小數點位置可能為負,這個負數應該正好是補零的 // 個數,所以小數點位置應該為 0 parts.splice(index > 0 ? index : 0, 0, "."); return parseFloat(parts.join("")); }
/** * 獲取小數位數 */ function getExponent(num) { return Math.floor(num) === num ? 0 : num.toString().split(".")[1].length; } /** * 兩數相加 */ function accAdd(num1, num2) { const multiple = Math.max(getExponent(num1), getExponent(num2)); return numScale(numScale(num1, multiple) + numScale(num2, multiple), multiple * -1); }
測試用例:
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 中小數和大整數的精度丟失
JavaScript 中的數字
計算機是怎么存儲數字的
為什么0.1無法被二進制小數精確表示?
為什么0.1+0.2=0.30000000000000004而1.1+2.2=3.3000000000000003?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91186.html
摘要:就像一些無理數不能有限表示,如圓周率,等。遵循規范,采用雙精度存儲,占用。參考中不會失去精度的最大值數字精度丟失的一些典型問題 問題描述 后端返回 { spaceObject: { objectId: 1049564069045993472 } } 前端模版,使用的是 atpl 模版 前端獲取 objectId 的方式,const objectId = $(#test).da...
摘要:推導為何等于在中所有數值都以標準的雙精度浮點數進行存儲的。先來了解下標準下的雙精度浮點數。精度位總共是,因為用科學計數法表示,所以首位固定的就沒有占用空間。驗證完成的最大安全數是如何來的根據雙精度浮點數的構成,精度位數是。 閱讀完本文可以了解到 0.1 + 0.2 為什么等于 0.30000000000000004 以及 JavaScript 中最大安全數是如何來的。 十進制小數轉為二...
摘要:基于這個問題運動基礎問題,我想應該也有一部分人沒有認真對待過中浮點數的四則運算出現的問題。解決方案引自解決方案為了解決浮點數運算不準確的問題,在運算前我們把參加運算的數先升級的的次方到整數,等運算完后再降級的的次方。 基于這個問題:javascript運動基礎問題 ,我想應該也有一部分人沒有認真對待過js中浮點數的四則運算出現的問題。 1.問題描述 示例代碼: var x ...
摘要:前言最近,朋友問了我這樣一個問題在中的運算結果,為什么是這樣的雖然我告訴他說,這是由于浮點數精度問題導致的。由于可以用階碼移動小數點,因此稱為浮點數。它的實現遵循標準,使用位精度來表示浮點數。 showImg(https://segmentfault.com/img/remote/1460000018981071); 前言 最近,朋友 L 問了我這樣一個問題:在 chrome 中的運算...
摘要:前言在數據敏感的業務場景中,常常會碰到數據精度問題,尤其在金額顯示占比統計等地方,該問題尤為顯著。計算機原理真香數值的精度問題,其實是非常基礎的計算機原理知識。前言 在數據敏感的業務場景中,常常會碰到數據精度問題,尤其在金額顯示、占比統計等地方,該問題尤為顯著。由于數據的每一位有效數字都包含真實的業務語義,一點點偏差甚至可能影響業務決策,這讓問題的嚴重性上升了幾個階梯。 那,什么是精度丟失...
閱讀 2574·2021-11-18 10:02
閱讀 1713·2021-09-30 10:00
閱讀 5310·2021-09-22 15:27
閱讀 1204·2019-08-30 15:54
閱讀 3671·2019-08-29 11:13
閱讀 2945·2019-08-29 11:05
閱讀 3319·2019-08-29 11:01
閱讀 569·2019-08-26 13:52