- Problems like this are encountered during development:
Let n1 = 0.1, n2 = 0.2 console.log(n1 + n2) // 0.30000000000000004Copy the code
- This is not the desired result. To equal 0.3, we need to transform it:
(n1 + n2). ToFixed (2) // Note that toFixed is roundedCopy the code
The toFixed(num) method rounds the Number to a specified decimal Number. So why did this happen?
Computers store data in binary format, so when a computer computes 0.1+0.2, it actually computes the binary sum of two numbers. The binary of 0.1 is 0.0001100110011001100… (1100 loop), 0.2 binary is: 0.00110011001100… (1100 cycles), the binary of both numbers are infinite cycles. So how does JavaScript handle an infinite loop of binary decimals?
We generally think of numbers as integers and decimals, but there is only one Number type in JavaScript: Number, which is implemented in accordance with IEEE 754 standards and is represented as a 64-bit fixed length, the standard double floating-point double. In binary scientific notation, the decimal part of a double-precision floating-point number can be reserved at most 52 digits, plus the 1 in front of it, which is actually 53 significant digits. The rest must be discarded, following the principle of “rounding zeros into ones”.
According to this principle, the binary numbers of 0.1 and 0.2 are added and converted to decimal: 0.30000000000000004.
So let’s seeHow are doubles saved:
- The first part (blue) : used to store sign bits, used to distinguish positive and negative numbers, 0 represents a positive number, occupying 1 bit
- The second part (green) is used to store exponents, occupying 11 bits
- The third part (red) is used to store decimal numbers
For 0.1, its binary is:
0.00011001100110011001100110011001100110011001100110011001 10011...
Copy the code
Convert to scientific notation (scientific notation results in floating point numbers) :
1.1001100110011001100110011001100110011001100110011001 * ^ 2-4Copy the code
It can be seen that the sign bit of 0.1 is 0, the exponential bit is -4, and the decimal bit is:
1001100110011001100110011001100110011001100110011001
Copy the code
So the question is, how do I store the negative digits?
The IEEE standard specifies an offset that is added to the exponential part each time, so that even if the exponential is negative, the offset is added to the positive number. Because JavaScript numbers are doubles, here we take the double as an example, its exponential part is 11 bits, can represent the range is 0 to 2047, IEEE fixed double offset is 1023.
-
When the index bits are not all zeros and not all ones (normalized values), IEEE provides that the formula for calculating the order code is e-bias. At this point, the minimum value of E is 1, then 1-1023= -1022, and the maximum value of E is 2046, then 2046-1023=1023. It can be seen that in this case, the value range is -1022~1013.
-
When all the index bits are 0 (the normalized value), IEEE defines the calculation formula of the order code as 1-bias, that is, 1-1023= -1022.
-
When all the exponent bits are 1 (special value), IEEE specifies that this floating-point number can be used to represent three special values, which are plus infinity, minus infinity, and NaN. Specifically, NaN is represented when the decimal place is not zero; When the decimal place is 0, the sign bit s=0 represents positive infinity, and s=1 represents negative infinity.
For the above index bit of 0.1 is -4, -4+1023 = 1019 is converted into binary: 1111111011. Therefore, 0.1 is expressed as:
0, 1111111011, 1001100110011001100110011001100110011001100110011001Copy the code
Having said that, it’s time to start with the question: how do 0.1+0.2=0.3?
A straightforward solution to this problem is to set a margin of error, often referred to as “machine accuracy.” For JavaScript, this value is usually 2-52. In ES6, the Number.EPSILON attribute is provided, and this value is 2-52. 0.1+0.2 ===0.3
function numberepsilon(arg1, arg2){
return Math.abs(arg1 - arg2) < Number.EPSILON;
}
console.log(numberepsilon(0.1 + 0.2, 0.3)); // true
Copy the code
Of course, there is another method that is more common:
function accAdd (arg1, arg2) {
let r1
let r2
let m
let n
try {
r1 = arg1.toString().split('.')[1].length
} catch (error) {
r1 = 0
}
try {
r2 = arg2.toString().split('.')[1].length
} catch (error) {
r2 = 0
}
m = Math.pow(10, Math.max(r1, r2))
n = (r1 > r2) ? r1 : r2
return ((arg1 * m + arg2 * m) / m).toFixed(n)
}
Copy the code