如何避免Javascript浮点计算精度问题
1 var num1 = 0.1, num2 = 0.2; 2 console.log(num1 + num2) // 0.30000000000000004
由于计算机内部的信息都是由二进制方式表示的,即0和1组成的各种编码,但由于某些浮点数没办法用二进制准确的表示出来。
1 var num1 = 0.1, num2 = 0.2; 2 console.log(num1.toString(2)) // 0.0001100110011001100110011001100110011001100110011001101 3 console.log(num2.toString(2)) // 0.001100110011001100110011001100110011001100110011001101
规避浮点计算
1 var handleAdd = function(num1, num2) { 2 num1 = Number(num1); 3 num2 = Number(num2); 4 var dec1, dec2, times; 5 try { dec1 = countDecimals(num1)+1; } catch (e) { dec1 = 0; } 6 try { dec2 = countDecimals(num2)+1; } catch (e) { dec2 = 0; } 7 times = Math.pow(10, Math.max(dec1, dec2)); 8 var result = (handleMul(num1, times) + handleMul(num2, times)) / times; 9 return result; 10 }; 11 12 var handleSub = function(num1, num2) { 13 num1 = Number(num1); 14 num2 = Number(num2); 15 var dec1, dec2, times; 16 try { dec1 = countDecimals(num1)+1; } catch (e) { dec1 = 0; } 17 try { dec2 = countDecimals(num2)+1; } catch (e) { dec2 = 0; } 18 times = Math.pow(10, Math.max(dec1, dec2)); 19 var result = Number((handleMul(num1, times) - handleMul(num2, times)) / times); 20 return result; 21 }; 22 23 var handleDiv = function(num1, num2) { 24 num1 = Number(num1); 25 num2 = Number(num2); 26 var t1 = 0, t2 = 0, dec1, dec2; 27 try { t1 = countDecimals(num1); } catch (e) { } 28 try { t2 = countDecimals(num2); } catch (e) { } 29 dec1 = convertToInt(num1); 30 dec2 = convertToInt(num2); 31 var result = handleMul((dec1 / dec2), Math.pow(10, t2 - t1)); 32 return result 33 }; 34 35 var handleMul = function(num1, num2) { 36 num1 = Number(num1); 37 num2 = Number(num2); 38 var times = 0, s1 = num1.toString(), s2 = num2.toString(); 39 try { times += countDecimals(s1); } catch (e) { } 40 try { times += countDecimals(s2); } catch (e) { } 41 var result = convertToInt(s1) * convertToInt(s2) / Math.pow(10, times); 42 return result 43 }; 44 45 var countDecimals = function(num) { 46 var len = 0; 47 try { 48 num = Number(num); 49 var str = num.toString().toUpperCase(); 50 if (str.split('E').length === 2) { // scientific notation 51 var isDecimal = false; 52 if (str.split('.').length === 2) { 53 str = str.split('.')[1]; 54 if (parseInt(str.split('E')[0]) !== 0) { 55 isDecimal = true; 56 } 57 } 58 let x = str.split('E'); 59 if (isDecimal) { 60 len = x[0].length; 61 } 62 len -= parseInt(x[1]); 63 } else if (str.split('.').length === 2) { // decimal 64 if (parseInt(str.split('.')[1]) !== 0) { 65 len = str.split('.')[1].length; 66 } 67 } 68 } catch(e) { 69 throw e; 70 } finally { 71 if (isNaN(len) || len < 0) { 72 len = 0; 73 } 74 return len; 75 } 76 }; 77 78 var convertToInt = function(num) { 79 num = Number(num); 80 var newNum = num; 81 var times = countDecimals(num); 82 var temp_num = num.toString().toUpperCase(); 83 if (temp_num.split('E').length === 2) { 84 newNum = Math.round(num * Math.pow(10, times)); 85 } else { 86 newNum = Number(temp_num.replace(".", "")); 87 } 88 return newNum; 89 }; 90 console.log(handleAdd(0.1, 0.2)) // 0.3 91 console.log(handleSub(1, 0.2)) // 0.8 92 console.log(handleDiv(2.3, 10)) // 0.23 93 console.log(handleMul(1.7, 10)) // 17
精度的思路: 将要进行计算的数值转化为整数进行计算,再将计算结果转化为小数。