<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        聊聊js的運算精度問題

        來源:懂視網 責編:小采 時間:2020-11-03 23:08:43
        文檔

        聊聊js的運算精度問題

        聊聊js的運算精度問題:都知道拿js去做運算肯定會遇到計算精準的問題(或稱舍入誤差),但是怎么避開這些坑,這里是我從網上整理的方案,歡迎探討。精準丟失的原因計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926…,1.333
        推薦度:
        導讀聊聊js的運算精度問題:都知道拿js去做運算肯定會遇到計算精準的問題(或稱舍入誤差),但是怎么避開這些坑,這里是我從網上整理的方案,歡迎探討。精準丟失的原因計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926…,1.333

        都知道拿js去做運算肯定會遇到計算精準的問題(或稱舍入誤差),但是怎么避開這些坑,這里是我從網上整理的方案,歡迎探討。

        1.jpg

        精準丟失的原因

        計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926…,1.3333… 等。JavaScript 使用 64 位存儲數字類型,因此超出的會舍去。舍去的部分就是精度丟失的部分。

        以下是十進制小數對應的二進制表示

        0.1 >> 0.0001 1001 1001 1001…(1001無限循環)
        0.2 >> 0.0011 0011 0011 0011…(0011無限循環)

        解決方案

        如需要更加復雜的計算類庫,可以考慮 math.js等知名類庫

        浮點數(小數)

        對于小數,前端出現問題的幾率還是很多的,尤其在一些電商網站涉及到金額等數據。解決方式:把小數放到位整數(乘倍數),再縮小回原來倍數(除倍數)轉換成整數后的運算結果 不能超過 Math.pow(2,53)

        // 0.1 + 0.2
        (0.1*10 + 0.2*10) / 10 == 0.3 // true

        浮點精準運算

        /**
         * floatObj 包含加減乘除四個方法,能確保浮點數運算不丟失精度
         *
         * ** method **
         * add / subtract / multiply /divide
         *
         * ** explame **
         * 0.1 + 0.2 == 0.30000000000000004 (多了 0.00000000000004)
         * 0.2 + 0.4 == 0.6000000000000001 (多了 0.0000000000001)
         * 19.9 * 100 == 1989.9999999999998 (少了 0.0000000000002)
         *
         * floatObj.add(0.1, 0.2) >> 0.3
         * floatObj.multiply(19.9, 100) >> 1990
         *
         */
        var floatObj = function() {
         /*
         * 判斷obj是否為一個整數
         */
         function isInteger(obj) {
         return Math.floor(obj) === obj
         }
         /*
         * 將一個浮點數轉成整數,返回整數和倍數。如 3.14 >> 314,倍數是 100
         * @param floatNum {number} 小數
         * @return {object}
         * {times:100, num: 314}
         */
         function toInteger(floatNum) {
         var ret = {times: 1, num: 0}
         if (isInteger(floatNum)) {
         ret.num = floatNum
         return ret
         }
         var strfi = floatNum + ''
         var dotPos = strfi.indexOf('.')
         var len = strfi.substr(dotPos+1).length
         var times = Math.pow(10, len)
         var intNum = parseInt(floatNum * times + 0.5, 10)
         ret.times = times
         ret.num = intNum
         return ret
         }
         /*
         * 核心方法,實現加減乘除運算,確保不丟失精度
         * 思路:把小數放大為整數(乘),進行算術運算,再縮小為小數(除)
         *
         * @param a {number} 運算數1
         * @param b {number} 運算數2
         * @param digits {number} 精度,保留的小數點數,比如 2, 即保留為兩位小數
         * @param op {string} 運算類型,有加減乘除(add/subtract/multiply/divide)
         *
         */
         function operation(a, b, digits, op) {
         var o1 = toInteger(a)
         var o2 = toInteger(b)
         var n1 = o1.num
         var n2 = o2.num
         var t1 = o1.times
         var t2 = o2.times
         var max = t1 > t2 ? t1 : t2
         var result = null
         switch (op) {
         case 'add':
         if (t1 === t2) { // 兩個小數位數相同
         result = n1 + n2
         } else if (t1 > t2) { // o1 小數位 大于 o2
         result = n1 + n2 * (t1 / t2)
         } else { // o1 小數位 小于 o2
         result = n1 * (t2 / t1) + n2
         }
         return result / max
         case 'subtract':
         if (t1 === t2) {
         result = n1 - n2
         } else if (t1 > t2) {
         result = n1 - n2 * (t1 / t2)
         } else {
         result = n1 * (t2 / t1) - n2
         }
         return result / max
         case 'multiply':
         result = (n1 * n2) / (t1 * t2)
         return result
         case 'divide':
         result = (n1 / n2) * (t2 / t1)
         return result
         }
         }
         // 加減乘除的四個接口
         function add(a, b, digits) {
         return operation(a, b, digits, 'add')
         }
         function subtract(a, b, digits) {
         return operation(a, b, digits, 'subtract')
         }
         function multiply(a, b, digits) {
         return operation(a, b, digits, 'multiply')
         }
         function divide(a, b, digits) {
         return operation(a, b, digits, 'divide')
         }
         // exports
         return {
         add: add,
         subtract: subtract,
         multiply: multiply,
         divide: divide
         }
        }();

        使用方法:

        floatTool.add(a,b);//相加
        floatTool.subtract(a,b);//相減
        floatTool.multiply(a,b);//相乘
        floatTool.divide(a,b);//相除

        超大整數

        雖然運算結果不超過Math.pow(2,53)的整數(9007199254740992)也可以使用上面的方法,但是如果就是有超過的呢,實際場景中可能會是一些批次號、號段之類的需求,這里我也找到了一個解決方案,直接上代碼。

        在線運算:https://www.shen.ee/math.html

        function compare(p, q) {
         while (p[0] === '0') {
         p = p.substr(1);
         }
         while (q[0] === '0') {
         q = q.substr(1);
         }
         if (p.length > q.length) {
         return 1;
         } else if (p.length < q.length) {
         return -1;
         } else {
         let i = 0;
         let a, b;
         while (1) {
         a = parseInt(p.charAt(i));
         b = parseInt(q.charAt(i));
         if (a > b) {
         return 1;
         } else if (a < b) {
         return -1;
         } else if (i === p.length - 1) {
         return 0;
         }
         i++;
         }
         }
        }
        function divide(A, B) {
         let result = [];
         let max = 9;
         let point = 5;
         let fill = 0;
         if (B.length - A.length > 0) {
         point += fill = B.length - A.length;
         }
         for (let i = 0; i < point; i++) {
         A += '0';
         }
         let la = A.length;
         let lb = B.length;
         let b0 = parseInt(B.charAt(0));
         let Adb = A.substr(0, lb);
         A = A.substr(lb);
         let temp, r;
         for (let j = 0; j < la - lb + 1; j++) {
         while (Adb[0] === '0') {
         Adb = Adb.substr(1);
         }
         if (Adb.length === lb) {
         max = Math.ceil((parseInt(Adb.charAt(0)) + 1) / b0); // 不可能取到這個最大值,1<= max <= 10
         } else if (Adb.length > lb) {
         max = Math.ceil((parseInt(Adb.substr(0, 2)) + 1) / b0);
         } else {
         result.push(0);
         Adb += A[0];
         A = A.substr(1);
         continue;
         }
         for (let i = max - 1; i >= 0; i--) {
         if (i === 0) {
         result.push(0);
         Adb += A[0];
         A = A.substr(1);
         break;
         } else {
         temp = temp || multiply(B, i + '');
         r = compare(temp, Adb);
         if (r === 0 || r === -1) {
         result.push(i);
         if (r) {
         Adb = reduce(Adb, temp);
         Adb += A[0];
         } else {
         Adb = A[0];
         }
         A = A.substr(1);
         break;
         } else {
         temp = reduce(temp, B);
         }
         }
         }
         temp = 0;
         }
         for (let i = 0; i < fill; i++) {
         result.unshift('0');
         }
         result.splice(result.length - point, 0, '.');
         if (!result[0] && result[1] !== '.') {
         result.shift();
         }
         point = false;
         let position = result.indexOf('.');
         for (let i = position + 1; i < result.length; i++) {
         if (result[i]) {
         point = true;
         break;
         }
         }
         if (!point) {
         result.splice(position);
         }
         result = result.join('');
         return result;
        }
        function multiply(A, B) {
         let result = [];
         (A += ''), (B += '');
         const l = -4; // 以支持百萬位精確運算,但速度減半
         let r1 = [],
         r2 = [];
         while (A !== '') {
         r1.unshift(parseInt(A.substr(l)));
         A = A.slice(0, l);
         }
         while (B !== '') {
         r2.unshift(parseInt(B.substr(l)));
         B = B.slice(0, l);
         }
         let index, value;
         for (let i = 0; i < r1.length; i++) {
         for (let j = 0; j < r2.length; j++) {
         value = 0;
         if (r1[i] && r2[j]) {
         value = r1[i] * r2[j];
         }
         index = i + j;
         if (result[index]) {
         result[index] += value;
         } else {
         result[index] = value;
         }
         }
         }
         for (let i = result.length - 1; i > 0; i--) {
         result[i] += '';
         if (result[i].length > -l) {
         result[i - 1] += parseInt(result[i].slice(0, l));
         result[i] = result[i].substr(l);
         }
         while (result[i].length < -l) {
         result[i] = '0' + result[i];
         }
         }
         if (result[0]) {
         result = result.join('');
         } else {
         result = '0';
         }
         return result;
        }
        function add(A, B) {
         let result = [];
         (A += ''), (B += '');
         const l = -15;
         while (A !== '' && B !== '') {
         result.unshift(parseInt(A.substr(l)) + parseInt(B.substr(l)));
         A = A.slice(0, l);
         B = B.slice(0, l);
         }
         A += B;
         for (let i = result.length - 1; i > 0; i--) {
         result[i] += '';
         if (result[i].length > -l) {
         result[i - 1] += 1;
         result[i] = result[i].substr(1);
         } else {
         while (result[i].length < -l) {
         result[i] = '0' + result[i];
         }
         }
         }
         while (A && (result[0] + '').length > -l) {
         result[0] = (result[0] + '').substr(1);
         result.unshift(parseInt(A.substr(l)) + 1);
         A = A.slice(0, l);
         }
         if (A) {
         while ((result[0] + '').length < -l) {
         result[0] = '0' + result[0];
         }
         result.unshift(A);
         }
         if (result[0]) {
         result = result.join('');
         } else {
         result = '0';
         }
         return result;
        }
        function reduce(A, B) {
         let result = [];
         (A += ''), (B += '');
         while (A[0] === '0') {
         A = A.substr(1);
         }
         while (B[0] === '0') {
         B = B.substr(1);
         }
         const l = -15;
         let N = '1';
         for (let i = 0; i < -l; i++) {
         N += '0';
         }
         N = parseInt(N);
         while (A !== '' && B !== '') {
         result.unshift(parseInt(A.substr(l)) - parseInt(B.substr(l)));
         A = A.slice(0, l);
         B = B.slice(0, l);
         }
         if (A !== '' || B !== '') {
         let s = B === '' ? 1 : -1;
         A += B;
         while (A !== '') {
         result.unshift(s * parseInt(A.substr(l)));
         A = A.slice(0, l);
         }
         }
         while (result.length !== 0 && result[0] === 0) {
         result.shift();
         }
         let s = '';
         if (result.length === 0) {
         result = 0;
         } else if (result[0] < 0) {
         s = '-';
         for (let i = result.length - 1; i > 0; i--) {
         if (result[i] > 0) {
         result[i] -= N;
         result[i - 1]++;
         }
         result[i] *= -1;
         result[i] += '';
         while (result[i].length < -l) {
         result[i] = '0' + result[i];
         }
         }
         result[0] *= -1;
         } else {
         for (let i = result.length - 1; i > 0; i--) {
         if (result[i] < 0) {
         result[i] += N;
         result[i - 1]--;
         }
         result[i] += '';
         while (result[i].length < -l) {
         result[i] = '0' + result[i];
         }
         }
         }
         if (result) {
         while ((result[0] = parseInt(result[0])) === 0) {
         result.shift();
         }
         result = s + result.join('');
         }
         return result;
        }

        使用方法:不可使用負數,參數最好使用字符串

        divide(A,B) // 除法
        multiply(A,B) //乘法
        add(A,B) //加法
        reduce(A,B) //減法

        toFixed 的修復

        在Firefox / Chrome中,toFixed并不會對于最后一位是5的如愿以償的進行四舍五入。

        1.35.toFixed(1) // 1.4 正確
        1.335.toFixed(2) // 1.33 錯誤
        1.3335.toFixed(3) // 1.333 錯誤
        1.33335.toFixed(4) // 1.3334 正確
        1.333335.toFixed(5) // 1.33333 錯誤
        1.3333335.toFixed(6) // 1.333333 錯誤

        Firefox 和 Chrome的實現沒有問題,根本原因還是計算機里浮點數精度丟失問題。

        修復方式:

        function toFixed(num, s) {
         var times = Math.pow(10, s)
         var des = num * times + 0.5
         des = parseInt(des, 10) / times
         return des + ''
        }

        本篇文章到這里就已經全部結束了,更多其他精彩內容可以關注PHP中文網的JavaScript視頻教程欄目!

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        聊聊js的運算精度問題

        聊聊js的運算精度問題:都知道拿js去做運算肯定會遇到計算精準的問題(或稱舍入誤差),但是怎么避開這些坑,這里是我從網上整理的方案,歡迎探討。精準丟失的原因計算機的二進制實現和位數限制有些數無法有限表示。就像一些無理數不能有限表示,如 圓周率 3.1415926…,1.333
        推薦度:
        標簽: js 計算 的計算
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: AV激情亚洲男人的天堂国语| 国内精品久久久久影院亚洲| 久久久久免费视频| 国产嫩草影院精品免费网址| 日韩国产欧美亚洲v片| 成熟女人牲交片免费观看视频| 亚洲成a人片7777| 国产日本一线在线观看免费| 亚洲乱码一二三四区乱码| 美女视频黄的全免费视频| 亚洲熟妇无码AV不卡在线播放| 成年女人免费碰碰视频| 国产亚洲高清在线精品不卡| 亚洲国产精品丝袜在线观看| 中国人免费观看高清在线观看二区 | 亚洲精品国产日韩| a级毛片无码免费真人| 亚洲av色香蕉一区二区三区蜜桃| 国产亚洲福利一区二区免费看| 成人精品综合免费视频| 亚洲乱码中文字幕久久孕妇黑人| 两个人看的www高清免费观看| 亚洲电影中文字幕| 日本亚洲免费无线码| 亚洲爆乳成av人在线视菜奈实| www.亚洲精品.com| 精品无码人妻一区二区免费蜜桃| 亚洲欧洲校园自拍都市| 日本免费无遮挡吸乳视频电影| 一级特黄a大片免费| 亚洲影院在线观看| 国产又大又黑又粗免费视频 | 日本特黄特色免费大片| 亚洲精品国产日韩无码AV永久免费网| 亚洲AV无码久久精品成人| 四虎永久在线精品免费网址| 成人久久久观看免费毛片| 亚洲综合网美国十次| 亚洲成片观看四虎永久| 最近新韩国日本免费观看| 精品亚洲视频在线|