1. Scene repetition

A classic interview question

0.1 + 0.2 === 0.3 // false
Copy the code

Why false?

Let’s start with this metaphor

For example, a number 1÷3=0.33333333……

3 will always be infinite loop, mathematics can be expressed, but the computer should be stored, convenient next time to take out and reuse, but 0.333333…… This number is infinite cycle, no matter how big the memory it can not store, so it can not store a value relative to mathematics, can only store an approximate value, when the computer storage and then take out the problem of accuracy loss

Floating-point numbers

“Floating point” is a standard for representing numbers, and integers can also be stored in floating-point format

We can also say that floating point numbers are decimals

In JavaScript, the prevailing numeric type is Number, which is encoded by 64-bit double-precision floating-point numbers in the IEEE754 specification

The advantage of this storage structure is that it can normalize the processing of integers and decimals and save storage space

An integer can be easily converted to decimal or binary. But for a floating point number, because of the decimal point, the position of the decimal point is not fixed. The idea is to use scientific notation, so that the decimal point is fixed

Computers, on the other hand, can only be represented in binary (0 or 1), which is converted to scientific notation by the following formula:

Where a is 0 or 1 and e is where the decimal point moves

Here’s an example:

27.0 converted to binary is 11011.0, which can be expressed in scientific notation as:

As mentioned earlier, javaScript stores double-precision floating-point numbers that are eight bytes long, or 64-bit bits

The 64-bit bits can be divided into three parts:

  • Sign bit S: The first bit is the sign bit (sign). 0 represents a positive number and 1 represents a negative number
  • E: The middle 11 bits store the exponent, which can be positive or negative. In double-precision floating-point numbers, the fixed offset of the exponent is 1023
  • Mantissa: The last 52 digits are mantissa. The excess digits are automatically zeros

As shown below:

Here’s an example:

27.5 Converts to binary 11011.1

11011.1 conversion to scientific notation

The sign bit is 1(positive), the exponential bit is 4+, 1023+4, that is, 1027

Because it is decimal need to convert to binary, namely 10000000011, the decimal part is 10111, fill up 52 bits namely: 1011 10000000 00000000 00000000 00000000 00000000 00000000 ‘

So 27.5 is stored as the computer’s binary standard form (sign bit + exponential bit + fractional part (order)), as shown below

0+10000000011+011 10000000 00000000 00000000 00000000 00000000 00000000

Ii. Problem analysis

Back to the question

0.1 + 0.2 === 0.3 // false
Copy the code

From the above, we know that in javascript, both 0.1 and 0.2 are converted to binary before operation

/ / 0.1 and 0.2 are converted into binary operations again after 0.00011001100110011001100110011001100110011001100110011010 + 0.0011001100110011001100110011001100110011001100110011010 = 0.0100110011001100110011001100110011001100110011001100111 / / In decimal it is exactly 0.30000000000000004Copy the code

So false

One more question, so why does x=0.1 get 0.1?

It is mainly the offset of the decimal point when storing the binary is up to 52 bits, and the number of bits that can be expressed is 2^53=9007199254740992, corresponding to the mandian of the scientific count is 9.007199254740992, which is the precision that JS can express at most

Its length is 16, so you can use toPrecision(16) to calculate the accuracy. If you exceed the accuracy, you will automatically round it up

. 10000000000000000555. ToPrecision (16) / / return 0.1000000000000000, remove just after the zero at the end of 0.1Copy the code

But what you see as 0.1 is not actually 0.1. Do not believe you can use a higher accuracy try:

0.1. ToPrecision (21) = 0.100000000000000005551Copy the code

What happens if the integer is greater than 9007199254740992?

Since the maximum exponent bit is 1023, the largest integer that can be represented is 2^ 1024-1, which is the largest integer that can be represented. But you can’t calculate it that way, because 2 to the 1024 becomes Infinity

> < span style = "box-sizing: border-box; color: RGB (51, 51, 51)Copy the code

So what happens to numbers between two to the 53 and two to the 63?

  • (2 ^ 2 ^ 53, 54)The number in between will be one of two, and it can only be an even number
  • (2 ^ 2 ^ 54, 55)The number between will be one of four, and can only accurately represent four multiples
  • . Skip more multiples of 2 in turn

To solve the problem of large numbers you can refer to the third party library bignumber.js, the principle is to treat all numbers as strings, re-implement the calculation logic, the disadvantage is that the performance is much worse than the original

summary

The computer stores double-precision floating-point numbers by converting the decimal number into binary scientific notation, and then storing the binary scientific notation in its own rules {sign bit +(exponent bit + binary of exponent offset)+ decimal part}

Because there is a bit limit on storage (64-bit), and some decimal floating-point numbers can be converted to binary in an infinite loop, resulting in binary rounding (0 rounding 1), which causes calculation errors when converted to decimal

Iii. Solutions

It’s theoretically impossible to store an infinite number of decimals in a finite amount of space, but we can manipulate it to get the result we want

When you have data like 1.4000000000000001 to display, it is recommended to use toPrecision and parseFloat to convert it to a number, as follows:

The parseFloat (1.4000000000000001 toPrecision (12)) = = = 1.4 / / TrueCopy the code

The encapsulation method is:

function strip(num, precision = 12) {
  return +parseFloat(num.toPrecision(precision));
}
Copy the code

For operational operations such as +-*/, you cannot use toPrecision. The correct way is to convert decimals into whole numbers and then operate. Take addition:

/ accurate addition * * * * / function add (num1, num2) {const num1Digits = (num1. The toString (). The split ('. ') [1] | | "). The length; const num2Digits = (num2.toString().split('.')[1] || '').length; const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits)); return (num1 * baseNum + num2 * baseNum) / baseNum; }Copy the code