“This is the 8th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

This article deals with binary conversion and IEEE 754 standards, so sometimes you will see a bunch of 000111 or some computer jargon, which may be a bit harsh for non-academic students, but it is necessary to be true, and it is explained in this article. Trust me, read patiently, you will have a harvest! 😀

The phenomenon of

Without further ado, get straight to the picture!

From the above figure, we know that the answer is not equal, and there is a very magical problem, 0.1+1-1 is not equal to 0.1, and the result of the first addition and the first subtraction and the first subtraction is also different!

What is JavaScript doing behind our backs? !

why

JavaScript is not entirely to blame for this problem. JavaScript uses IEEE 754 floating-point arithmetic, which results in rounding errors. All IEEE 754 compliant languages have this problem when performing floating-point operations.

Error generating process

Now let’s uncover the process that creates the error. The first thing we need to know here is that when you do floating-point operations you need to convert them to binary and then do the operations. So how do decimal floating-point numbers convert to binary?

The process of converting a floating point number to binary is as follows: 1. Mod /2 is used for integer parts

1 => 1/2 = 0 1 left, so 3 (decimal) = 11 (binary)Copy the code
2 => 2%2 = 10 left 1 => 1%2 = 0 left 1 so 4 (decimal) = 100 (binary)Copy the code

2. The decimal part is rounded by *2

0.5 => 0.5*2 = 1 Integer 1 0.5 (decimal) = 0.1 (binary)Copy the code
0.1 => 0.1*2 = 0.2 Rounded 0 0.2 => 0.2*2 = 0.4 Rounded 0 0.4 => 0.4*2 = 0.8 rounded 0 0.8 => 0.8*2 = 1.6 Rounded 1 0.6 => 0.6*2 = 1.2 Rounded 1 0.2 = 0.2 * 2 = 0.4 > integer 0 = 0.4 > 0.4 * 2 = 0.8 integer 0 = 0.8 > 0.8 * 2 = 1.6 integer 1 = 0.6 > 0.6 * 2 = 1.2 integer 1... Cycle results 0.1 (decimal) = 00011001100110011001100110011... (0011) Loop (binary)Copy the code

Similarly, the binary conversion of both integer and decimal values is the binary conversion of the integer and decimal parts, and then add.

As you can see in the above example, 0.1 to binary is an infinite loop. The mantras of IEEE 754 can only hold 52 significant digits (for reasons we’ll explain later), so 0.1 to binary is rounded, resulting in an error.

Before we get to the arithmetic, we need two things:

  1. Converting a decimal floating-point number to the mantissa after binary52The significant number goes from the first one1Start holding back52Significant numbers, so what you’re going to find is0.10.2keep52The length will vary after the mantissa.
  2. During rounding, follow0 1 intoThe rules.
  3. In order to facilitate your understanding of the following process, I reserve for all52Not after the mantissa52In the case of zero, part of the numbers in order to facilitate the operation of more than52Complement and transform (e.g. 1).

Let’s take a look at this example:

0.1 turn binary 0.0001100110011001100110011001100110011001100110011001100110011 keep 52 mantissa 0.00011001100110011001100110011001100110011001100110011010 0.2 binary 0.001100110011001100110011001100110011001100110011001100110011 keep 52 mantissa 0.0011001100110011001100110011001100110011001100110011010 in addition 0.00011001100110011001100110011001100110011001100110011010 0.0011001100110011001100110011001100110011001100110011010 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 0.01001100110011001100110011001100110011001100110011001110 Combined the results of the retention 52 mantissa turn the decimal 0.30000000000000004 0.010011001100110011001100110011001100110011001100110100Copy the code

The following is the calculation process of 0.1+1-1:

0.1 turn binary 0.0001100110011001100110011001100110011001100110011001100110011 keep 52 mantissa 0.00011001100110011001100110011001100110011001100110011010 1 binary and keep 52 mantissa Add 0.00011001100110011001100110011001100110011001100110011010 1.0000000000000000000000000000000000000000000000000000 1.0000000000000000000000000000000000000000000000000000 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1.00011001100110011001100110011001100110011001100110011010 the result of the combined retaining 52 mantissa 1.0001100110011001100110011001100110011001100110011010 will be a reduction of 1, 0.00011001100110011001100110011001100110011001100110100000 Convert to decimal 0.10000000000000009Copy the code

The following is the operation process of 0.1-1+1:

0.1 turn binary 0.0001100110011001100110011001100110011001100110011001100110011 keep 52 mantissa 0.00011001100110011001100110011001100110011001100110011010 1 binary and keep 52 mantissa Subtract 1.0000000000000000000000000000000000000000000000000000, here is equivalent to 1-0.1 turns negative For the convenience of subtraction, We will be 1 binary conversion and zero padding is 0.11111111111111111111111111111111111111111111111111111120 0.00011001100110011001100110011001100110011001100110011010 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 0.11100110011001100110011001100110011001100110011001100110 here is a closer to 0.9 the results of negative number after subtracting the reserved 52 mantissa 0.11100110011001100110011001100110011001100110011001101-0.9 + 1 at this time is equivalent to 1-0.9 in the same way, for the convenience of subtraction, We will be 1 binary conversion and zero padding is 0.11111111111111111111111111111111111111111111111111112 0.11100110011001100110011001100110011001100110011001101 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 0.00011001100110011001100110011001100110011001100110011 would retain the results of 52 mantissa Turn the decimal 0.09999999999999998 0.00011001100110011001100110011001100110011001100110011000Copy the code

Now that we understand the operations in the example and understand the reasons for these situations, let’s talk about IEEE 754 to unravel:

  1. What is the mantissa
  2. Why is itFifty-two mantissa digits
  3. why0 1 into

And more floating point mysteries

IEEE 754

Double-precision floating-point numbers in IEEE 754 are stored using 64 bits:

  • The first store symbol represents the plus and minus signs 0, 1 and minus
  • The 2-12 digit storage exponent represents the power number
  • 13-64 – bit mantissa indicates accuracy

There is nothing to be said for sign bits, which are used to represent positive and negative numbers. The exponent bits represent the power number, which is based on the current base, such as 5:

  • If the current value is decimal, yes105 the power
  • If it is currently binary, yes25 the power

The mantissa bits store the mantissa to indicate the accuracy of a value greater than or equal to 1 and less than 2

To sum up, if we use S to represent positive and negative signs, H to represent base numbers, e to represent power numbers, and f to represent mantissa, the floating point value can be expressed as:


v a l u e = s f h e value = s*f*h^e

I believe that by this step, friends will understand the exponential and mantissa more clearly, and also explain the first two questions.

  1. The mantissa digit is 164 bitThe part of a floating point number that stores the mantissa and indicates the accuracy of a numerical value
  2. 52Is in theIEEE 754That’s the standard policy

In our direct binary operation above, we are actually a result of mixing the exponent and mansa bits, so we keep 52 bits, which are 52 significant digits after the first 1. I don’t know if you noticed a problem: the mantissa only has 52 digits, but now we keep 52 significant digits after the first 1, so if we add the preceding 1, we have 53 digits. This is because the integer part of the mantissa must be a 1, so in order to take full advantage of the 52-bit space for higher accuracy, the integer part that must be equal to 1 can be omitted. 52 bits are used to represent decimals.

Maximum safe integer

Similarly, since there are only 52 mantissa, the largest safe integer in JavaScript is 2^53-1, where 53 is the 52 mantissa plus the previously omitted 1, and -1 is because 2^53 is already a bounding value, anything greater than that equals it, So the largest safe integer is 2^53-1.

Rounding rules

The IEEE 754 standard lists four different methods:

  • Round To nearest: Round To nearest, Even (this is the default rounding) : Results are rounded To the nearest and representable value, but when there are two numbers that are equally close, the Even number (ending in 0 in binary) is taken.
  • Rounding toward +∞ : Results are rounded toward positive infinity.
  • Rounding toward -∞ : Results are rounded in the direction of negative infinity.
  • Rounding to 0: Results are rounded to 0.

The first rule (the default rounding) can be understood simply as rounding, which translates to our binary floating-point operation, rounding zero to one.

The solution

  1. useJavaScriptThe minimum accuracy value provided determines whether the error is within the range

    Math. Abs (0.1 + 0.2-0.3) <= number.epsilon
  2. Convert to an integer and then convert back to a decimal
  3. Keep a few decimal places like the amount, just to the minute
  4. Use someone else’s wheel, for example:math.js
  5. Convert to string addition (less efficient)

Refer to documentation and tools website

Baidu Encyclopedia Wikipedia decimal to IEEE 754 floating point binary conversion tool

PS

In order to reduce the cost of reading for friends, there is no introduction to the knowledge of index calculation and scientific counting.

If you have any questions or suggestions, please leave a comment! 👏 🏻 👏 🏻 👏 🏻