The cause of

Recently, in the project-line environment, customer feedback was collected and the display value was 0.01 less after input. After receiving the feedback, the problem was immediately located in the code, because in the previous version, the input box of the value was changed to non-rounded and two decimal points were cut. The method was as follows

Math.floor(floatNum*100) /100
Copy the code

When floatNum is 1.15, an exception is raised due to the accuracy of the Js floatNum.

You can see that 1.15*100 evaluates to 114.99999999999999 instead of 115 in Js, which results in 1.14 when rounded down by math.floor () and divided by 100, exactly 0.01 away from the expected result.

Fix the problem as soon as you locate it, decide to discard the floating-point calculation and use a conversion to string interception.

/** * keep decimals (not rounded) *@param FloatNum Indicates the input value *@param Decimal preserves floating point precision digits */
formatDecimal(floatNum, decimal) {
    let result = floatNum.toString();
    const index = result.indexOf('. ');
    if(index ! = = -1) {
      result = result.substring(0, decimal + index + 1);
    } else {
      result = result.substring(0);
    }
    return parseFloat(result);
  }
Copy the code

After the problem was solved, we decided to sort out THE Js floating point numbers and list some possible accuracy problems.

Js floating point number

Standards and storage methods

Js has no separate floating point type. Floating point Number and integer are represented by Number type, which is a 64-bit double precision value in accordance with IEEE754 standard. In binary, 64-bit consists of three parts.

  • Sign bit (S) : Used to indicate a sign, 0 for a positive number and 1 for a negative number

  • Exponent (E) : Used to express the number to the power, with the middle 11 digits

  • Mantissa (mantissa M) : used to indicate accuracy, the part beyond the automatic into a circle of zeros, the last 52 digits

In binary scientific notation, IEEE754 standard 64-bit double digit values are expressed by the formula:

In the decimal scientific notation, 1 <= M < 10, and in the binary scientific notation, 1 <= M < 2, so the integer part of M can only be 1, which can be omitted, and M only stores the following decimal part. The index E is 11 bits, and the number represented in the big energy is 2047 (2^ 11-1). Since the index contains positive and negative, the median value 1023 is the critical value. [1024, 2047] represents the positive index, and [0, 1022] represents the negative index.

For example, 4.5 is converted to binary 100.1, which in binary scientific notation is 1.001 * 2^2. Substituted into the formula 1.001, the mantras M is 001, the index E is 2 + 1023 = 1025 (binary 10000000001), and the positive S takes 0, so the combination of 4.5 in IEEE754 double precision 64-bit standard is expressed as follows.

The picture is derived frombinaryconvert

What are the common accuracy problems and behind the principle

Understand the standard and storage of floating point number in Js, and then go back to the actual scene to see what common floating point number accuracy problems and the reasons.

The scene of a0.1 + 0.2 != 0.3

The binary representation of 0.1, 0.2, and the summation is shown below

0.1 -> 0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 (1001 is cycled into zeros) 0.2 -> 0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 010 (0011 cycle into a shed zero) additive - > 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 111Copy the code

The added value in decimal is exactly 0.30000000000000004

(Decimal decimal to binary decimal calculation)

(Decimal decimal to binary decimal calculation tool)

Scenario 2(1.335). ToFixed (2) = = 1.33

Floating point accuracy and toFixed actually belong to the same class of problems, both caused by floating point number can not be accurately expressed, as follows:

(1.335).toPrecision(20);    / / "1.3349999999999999645"
Copy the code

The toFixed() method actually keeps two decimal places rounded to 1.3349999999999999645

Scenario three major number problems61453901951867050 + 5 == 61453901951867060

Because Javascript’s numeric store uses the double-precision floating-point data type specified in IEEE 754, this data type can safely store values (including boundary values) between (2^ 53-1) and -(2^ 53-1).

Number.MAX_SAFE_INTEGER === Math.pow(2.53) - 1 // true Indicates the maximum safe integer
Number.MIN_SAFE_INTEGER === -(Math.pow(2.53) - 1) // true The minimum safe integer
Copy the code

Safe storage here means the ability to accurately distinguish between two different values, for example:

Number.MAX_SAFE_INTEGER + 1= = =Number.MAX_SAFE_INTEGER + 2 // true
Copy the code

The result is true, which is mathematically incorrect because the largest safe integer in JavaScript is number.max_safe_integer following the IEEE754 standard

In scenario 3, the value 61453901951867050 exceeds the maximum safe integer, resulting in inaccurate summation results.

61453901951867050 > Number.MAX_SAFE_INTEGER // true
Copy the code

Solutions to precision problems

Suppose we want to determine whether 0.1 + 0.2 equals 0.3

The absolute value of the difference between two numbers is less than Number.EPSILON

Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON // true
Copy the code

EPSILON considers the equation to be true if it is less than Number.

However, this method can only be used to determine whether the equations on both sides are true. If you need to calculate the exact value of a floating point number, you need to use other methods.

There are already many mature libraries in the community, such as Bignumber.js, decimal.js, and big.js. We can choose the tools according to our own needs. These libraries not only solve the accuracy problem of floating-point numbers, but also support large numbers, and fix the inaccurate results of native toFixed.

conclusion

Thank you for reading. If there are any shortcomings, please correct them.

Happy XDM 1024 sections in advance ~

reference

Wikipedia-IEEE754

Js float precision is a cliche

JS floating point number precision problem