Reason: js processes the addition, subtraction, multiplication and division of decimals according to 2. On the basis of arg1, the accuracy of arg2 is expanded or inversely expanded, so the following situation will occur.
The problem of adding, subtracting, multiplication and division of decimal points in javascript (js) is a bug in js such as 0.3*1 = 0.299999999999, etc. The following lists four js algorithms that can perfectly calculate the corresponding accuracy.
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 accMul((r1/r2),pow(10,t2-t1)); } } //Multiple 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) } //Add function accAdd(arg1,arg2){ var r1,r2,m; 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)) return (arg1*m+arg2*m)/m } //Subtraction function Subtr(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)); n=(r1>=r2)?r1:r2; return ((arg1*m-arg2*m)/m).toFixed(n); }Let's analyze the loss of digital precision in JavaScript.
1. Some typical problems of JS digital precision loss
1. Add two simple floating point numbers
0.1 + 0.2 != 0.3 // true
Firebug
This is really not a Firebug problem, you can try it with alert (haha, kidding).
Check out Java's calculation results
Take a look at Python
2. Large integer operation
999999999999999999 == 10000000000000000001 // ?
Firebug
The 16-digit and 17-digit numbers are actually equal, it's unreasonable.
Also like
var x = 9007199254740992x + 1 == x // ?
See the results
The three views were subverted again.
3. toFixed will not round (Chrome)
1.335.toFixed(2) // 1.33
Firebug
There have been inconsistent prices in Chrome and other browsers online, and it is precisely because of the toFixed compatibility issue.
2. Reasons for JS number loss accuracy
Some numbers cannot be expressed in finitely by computer binary implementations and bit limits. Just like some irrational numbers cannot be finitely represented, such as Pi 3.1415926..., 1.3333..., etc. JS follows the IEEE 754 specification, adopts double precision storage, and occupies 64 bits. As shown
significance
Floating point numbers, e.g.
0.1 >> 0.0001 1001 1001 1001…(1001 infinite loop) 0.2 >> 0.0011 0011 0011 0011…(0011 infinite loop)
At this time, you can only round it by imitating the decimal system, but there are only two binary systems: 0 and 1, so it becomes 0 and round it by 1. This is the fundamental reason why some floating-point numbers in computers are errors and lost accuracy.
The accuracy loss of large integers is essentially the same as floating point numbers. The maximum mantissa bit is 52 bits, so the maximum integer that can be accurately represented in JS is Math.pow(2, 53), which is 9007199254740992.
More than 9007199254740992 may lose accuracy
9007199254740992 >> 1000000000000000...000 // Total 53 09007199254740992 + 1 >> 10000000000000000...001 // In the middle 52 09007199254740992 + 2 >> 10000000000000000...010 // In the middle 51 0
In fact
9007199254740992 + 1 // Lost 9007199254740992 + 2 // Not lost 9007199254740992 + 3 // Lost 9007199254740992 + 4 // Not lost
The results are shown in the figure
As mentioned above, we can know that the seemingly finite numbers are infinite in the binary representation of the computer. Due to the storage bit limit, there is "got away" and the accuracy loss occurs.
For more in-depth analysis, you can read this paper (long and smelly): What Every Computer Scientist Should Know About Floating-Point Arithmetic
3. Solution
For integers, the probability of problems in the front-end may be relatively low. After all, few businesses need to use super-large integers. As long as the calculation result does not exceed Math.pow(2, 53), the accuracy will not be lost.
For decimals, there are still many chances of problems with the front-end, especially in some e-commerce websites that involve data such as amounts. Solution: Put the decimal into the integer (multiple multiple), and then shrink back to the original multiple (dividing multiple)
// 0.1 + 0.2(0.1*10 + 0.2*10) / 10 == 0.3 // true
The following is an object I wrote to block the loss of accuracy of addition, subtraction, multiplication and division of decimals. Of course, the converted integer cannot exceed 9007199254740992.
/*** floatObj contains four methods: addition, subtraction, multiplication and division, which can ensure that floating-point number calculation does not lose accuracy** We know that floating-point number calculations will have accuracy loss (or rounding error). The root cause is that some numbers cannot be expressed in binary and implementing bit limits* The following is the binary representation corresponding to decimal decimal * 0.1 >> 0.0001 1001 1001 1001 1001…(1001 infinite loop)* 0.2 >> 0.0011 0011 0011 0011…(0011 infinite loop)* The storage of each data type in a computer is a finite width. For example, JavaScript uses 64-bit storage numeric types, so those that exceed them will be discarded. The part that is lost is the part that is lost in accuracy. ** ** method *** add / subtract / multiply /divide** ** explanation *** 0.1 + 0.2 == 0.300000000000000004 (0.0000000000000004 more) * 0.2 + 0.4 == 0.60000000000000000001 (0.00000000000000001) * 19.9 * 100 == 1989.9999999999999998 (0.00000000000000002 less) ** floatObj.add(0.1, 0.2) >> 0.3* floatObj.multiply(19.9, 100) >> 1990**/var floatObj = function() {/** Determine whether obj is an integer*/function isInteger(obj) {return Math.floor(obj) === obj}/** Convert a floating point number into an integer and return an integer and multiple. For example, 3.14 >> 314, the multiple is 100* @param floatNum {number} decimal* @return {object}* {times:100, num: 314}*/function toInteger(floatNum) {var ret = {times: 1, num: 0}if (isInteger(floatNum)) {ret.num = floatNumreturn ret}var strfi = floatNum + ''var dotPos = strfi.indexOf('.')var len = strfi.substr(dotPos+1).lengthvar times = Math.pow(10, len)var intNum = parseInt(floatNum * times + 0.5, 10)ret.times = timesret.num = intNumreturn ret}/** Core method to implement addition, subtraction, multiplication and division operations to ensure that accuracy is not lost* Idea: enlarge the decimal into an integer (multiplier), perform arithmetic operations, and then shrink to a decimal (divided)** @param a {number} Operation number 1* @param b {number} Operation number 2* @param digits {number} Accuracy, the number of reserved decimal points, such as 2, is retained as two decimal places* @param op {string} Operation type, with addition, subtraction, multiplication and division (add/subtract/multiply/divide)**/function operation(a, b, digits, op) {var o1 = toInteger(a)var o2 = toInteger(b)var n1 = o1.numvar n2 = o2.numvar t1 = o1.timesvar t2 = o2.timesvar max = t1 > t2 ? t1 : t2var result = nullswitch (op) {case 'add':if (t1 === t2) { // Two decimal places are the same result = n1 + n2} else if (t1 > t2) { // o1 decimal places greater than o2result = n1 + n2 * (t1 / t2)} else { // o1 decimal places less than o2result = n1 * (t2 / t1) + n2}return result / maxcase 'subtract':if (t1 === t2) {result = n1 - n2} else if (t1 > t2) {result = n1 - n2 * (t1 / t2)} else {result = n1 * (t2 / t1) - n2}return result / maxcase 'multiply':result = (n1 * n2) / (t1 * t2) return resultcase 'divide':result = (n1 / n2) * (t2 / t1)return result}}// Four interfaces of addition, subtraction, multiplication and division 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')}// exportsreturn {add: add,subtract: subtract,multiply: multiply,divide: divide}}();ToFixed's fix is as follows
// toFixed Fix function toFixed(num, s) {var times = Math.pow(10, s)var des = num * times + 0.5des = parseInt(des, 10) / timesreturn des + ''}