0.1 + 0.2! = 0.3 in JS

Time:2020-1-17

I’ve been asked about the result of 0.1 + 0.2 in an interview. At first glance, you may think about this question, isn’t it 0.3? But you’ll think it’s not that easy. Let’s have a look today.

1. The storage mechanism of numbers in JavaScript

In JavaScript, numbers are stored as IEEE 754 double precision 64 bit floating-point numbers (which need to turn over the wall), and its representation format is:

(s) * (m) * (2 ^ e)

Where s stands for sign bit, m stands for mantissa, accounting for 52 bits, e stands for index, accounting for 11 bits. According to ECMAScript 5 specification, the range of E is[-1074, 971]In this way, it can be concluded that the maximum value that JS can represent is 1 * (2 ^ 53 – 1) * (2 ^ 971) = 1.7976931348623157e + 308, and this value is exactlyNumber.MAX_VALUESimilarly, we can deduce that the minimum value that JS can represent is 1 * 1 * (2 ^ – 1074) = 5e-324, which happens to beNumber.MIN_VALUEThe value.

Here you may notice that M is 52 bits, and the maximum value that can be expressed is 2 ^ 52 – 1, but why is 53 here? This involves the hidden bit. For example, in 1 * 1.00111100 * 2 ^ – 3, M represents the decimal part “00111100” in 1.00111100, and the integer part 1 is the hidden bit. That is to say, as long as the index is not all 0, its hidden bit is 1. By the way, the range of integers in JS that can be expressed precisely is-2 ^ 53 + 1 ~ 2 ^ 53 - 1

2. Digital representation

Now let’s go back to this topic. We know that in computers, numbers are stored in binary, so we should first convert 0.1 and 0.2 into binary. For decimal to binary, the integer partDivide by two to get the rest and arrange in reverse order, decimal partMultiply by two to round, arrange in orderSo

Convert 0.1 to binary
0.0 0011 0011 0011 0011 0011 0011… (0011 cycle)

0.2 to binary
0.0011 0011 0011 0011 0011 0011 0011… (0011 cycle)

Then we use the IEEE 754 double precision 64 bit floating-point number (need to turn over the wall) to express:

// 0.1
e = -4;
M = 1.1001100110011001100110011001100110011001100110011010 (52 digits)

// 0.2
e = -3;
M = 1.1001100110011001100110011001100110011001100110011010 (52 digits)

Of course, m here refers to 52 digits after the decimal point, and the integer part 1 before the decimal point is the hidden digit mentioned above.

Then we add it up. There is a problem here, which is how to deal with the exponential inconsistency. Generally, we move to the right, because even if the right overflows, the accuracy of the loss is far less than that of the left.

E = – 4; m = 1.10011001100110011001100110011001100110011001100110011010 (52 digits)

+

E = – 3; m = 1.10011001100110011001100110011001100110011001100110011010 (52 digits)


E = – 3; m = 0.11001100110011001100110011001100110011001100110011001101 (52 digits)

+

E = – 3; m = 1.10011001100110011001100110011001100110011001100110011010 (52 digits)


E = – 3; m = 10.0110011100110011001100110011001100110011001100110011001100111 (52 digits)


E = – 2; m = 1.001100110011001100110011001100110011001100110011001100111 (53 digits)

We see that it has overflowed (more than 52 digits), so we have to do rounding at this time, so how can rounding be the closest to the original number? For example, if you want to keep 2 decimal places for 1.101, the result may be 1.10 and 1.11. At this time, both of them are the same. Which one should we choose? The rule is to keep the even one, here is to keep 1.10.

Back to us, M = 1.00110011001100110011001100110011001100110011001100110100 (52 digits)

And then we get the final binary number

1.0011001100110011001100110011001100110011001100110100 * 2 ^ -2

=0.010011001100110011001100110011001100110011001100110100

Now convert to decimal. The way to convert binary decimal to decimal is after decimal pointFirst place * 2 ^ - 1Second place * 2 ^ - 2And so on. Finally, we get the decimal number as0.30000000000000004, so the final result of 0.1 + 0.2 is

0.30000000000000004!

This is the whole reasoning process. I hope you can also try to reason. Although this is a partial knowledge in JavaScript, it’s still good for understanding the storage mechanism of the computer.