Recently when doing the project, involved in the calculation of product prices, often JS floating point precision problem, this problem, for developers, financial management system is a very serious problem, which involves money related problem are serious problem), here the related problems and the reason of solution to tidy it up, also hope to provide some references for you.

I. Common examples

   / / add
   0.1 + 0.2 = 0.30000000000000004
   0.1 + 0.7 = 0.7999999999999999
   0.2 + 0.4 = 0.6000000000000001

   / / subtraction
   0.3 - 0.2 = 0.09999999999999998
   1.5 - 1.2 = 0.30000000000000004

   / / the multiplication
   0.8 * 3 = 2.4000000000000004
   19.9 * 100 = 1989.9999999999998

   / / division
   0.3 / 0.1 = 2.9999999999999996
   0.69 / 10 = 0.06899999999999999

   / / to compare
   0.1 + 0.2= = =0.3 // false
   (0.3 - 0.2) = = = (0.2 - 0.1) // falseCopy the code

Two. Causes

JavaScript has only one Number type inside of it, that is, there are no integers at all at the bottom of the JavaScript language. All numbers are stored as 64-bit floating-point numbers in ieEE-754 format. 1 is the same as 1.0. Because some decimals are written in binary and the number of digits is infinite. JavaScript will discard binaries beyond 53 bits, so be careful with decimal comparisons and calculations.

IEEE Standard for Binary Floating-point Arithmetic (IEEE 754)

IEEE Binary Floating-point arithmetic Standard (IEEE 754) is the most widely used floating-point arithmetic standard since the 1980s. It is adopted by many cpus and floating-point arithmetic machines. The standard defines formats for representing floating-point numbers (including negative zero-0 and denormal number), special values (Inf and NaN), and “floating-point operators” for these numbers. It also specifies four value rounding rules and five exceptions (including when and how exceptions are handled).

Storage of floating point numbers

The floating-point number of JS also follows IEEE 754 standard and adopts double precision storage, which is represented by a fixed length of 64 bits, in which 1 bit is used to represent the sign bit, 11 bits are used to represent the exponent, and 52 bits are used to represent the mantissa. The diagram below:

  • Sign bit (sign) : The first bit is the sign bit for positive and negative numbers. 0 represents a positive number and 1 represents a negative number
  • Exponent: The middle 11 bits store the Exponent, which is used to express the Exponent
  • Mantissa: The last 52 mantissa digits are automatically zeros

5. Calculation steps of floating point numbers (0.1+0.2)

[1] First, decimal 0.1 and 0.2 are converted to binary, but since floating point numbers are infinite in binary notation

 0.1-- >0.0001 1001 1001 1001. (1001Loop)0.2-- >0.0011 0011 0011 0011. (0011Loop)Copy the code

[2] IEEE754 supports a maximum of 53 bits of binary for the fractional part of a 64-bit double precision floating-point number. The excess binary digits are truncated, so the sum of the binary digits after the addition of the two is

 0.0100110011001100110011001100110011001100110011001101Copy the code

[3] When the truncated binary number is converted to decimal, it becomes 0.30000000000000004, so there is an error in the calculation

Vi. Solutions

[1] Reference class library

  1. Math.js
  2. decimal.js
  3. big.js

[2] Idea 1: If you know the number of decimal places, you can enlarge the floating point number to an integer (and then divide by the corresponding multiple), and then perform the operation. In this way, you can get the correct result

   0.1 + 0.2- > (0.1 * 10 + 0.2 * 10) / 10 / / 0.3
   0.8 * 3- > (0.8 * 100 * 3) / 100         / / 2.4Copy the code

[3] Customize a conversion and processing function

    // f represents the expression to be evaluated, and digit represents the decimal number
    Math.formatFloat = function (f, digit) {
      // math.pow (exponent, power exponent)
      var m = Math.pow(10, digit);
      // math.round ()
      return Math.round(f * m, 10) / m;
    }
    console.log(Math.formatFloat(0.3 * 8.1));  / / 2.4
    console.log(Math.formatFloat(0.35 * 8.2));  / / 2.8Copy the code

[4] Addition function

    /** ** add function, used to get exact add result ** Note: javascript add result will have error, when two floating point numbers add will be more obvious. This function returns a more accurate addition result. ** Calls: accAdd(arg1,arg2) ** Returns the exact result of arg1 plus arg2 **/
    function accAdd(arg1, arg2) {
      var r1, r2, m, c;
      try {
        r1 = arg1.toString().split(".") [1].length;
      } catch (e) {
        r1 = 0;
      }
      try {
        r2 = arg2.toString().split(".") [1].length;
      } catch (e) {
        r2 = 0;
      }
      c = Math.abs(r1 - r2);
      m = Math.pow(10.Math.max(r1, r2));
      if (c > 0) {
        var cm = Math.pow(10, c);
        if (r1 > r2) {
          arg1 = Number(arg1.toString().replace(".".""));
          arg2 = Number(arg2.toString().replace("."."")) * cm;
        } else {
          arg1 = Number(arg1.toString().replace("."."")) * cm;
          arg2 = Number(arg2.toString().replace("."."")); }}else {
        arg1 = Number(arg1.toString().replace(".".""));
        arg2 = Number(arg2.toString().replace(".".""));
      }
      return (arg1 + arg2) / m;
    }

    // Add an add method to the Number type to make it easier to call.
    Number.prototype.add = function (arg) {
      return accAdd(arg, this);
    };Copy the code

[5] Subtraction functions

    /** ** subtraction function, used to get exact subtraction results ** Note: javascript subtraction results can be error, when two floating point numbers subtracting will be more obvious. This function returns a more accurate subtraction. ** Calls: accSub(arg1,arg2) ** Returns the exact result of arg1 plus arg2 **/
    function accSub(arg1, arg2) {
      var r1, r2, m, n;
      try {
        r1 = arg1.toString().split(".") [1].length;
      } catch (e) {
        r1 = 0;
      }
      try {
        r2 = arg2.toString().split(".") [1].length;
      } catch (e) {
        r2 = 0;
      }
      m = Math.pow(10.Math.max(r1, r2)); //last modify by deeka //
      n = (r1 >= r2) ? r1 : r2;
      return ((arg1 * m - arg2 * m) / m).toFixed(n);
    }

    // Add a mul method to the Number type to make it easier to call.
    Number.prototype.sub = function (arg) {
      return accMul(arg, this);
    };Copy the code

[6] The multiplication function

    /** ** multiplication function, used to get exact multiplication results ** Note: javascript multiplication results have errors, which can be obvious when two floating point numbers are multiplied. This function returns a more accurate multiplication result. ** Call: accMul(arg1,arg2) ** Return value: arg1 times the exact result of arg2 **/
    function accMul(arg1, arg2) {
      var m = 0,
        s1 = arg1.toString(),
        s2 = arg2.toString();
      try {
        m += s1.split(".") [1].length;
      } catch (e) {}
      try {
        m += s2.split(".") [1].length;
      } catch (e) {}
      return Number(s1.replace("."."")) * Number(s2.replace("."."")) / Math.pow(10, m);
    }

    // Add a mul method to the Number type to make it easier to call.
    Number.prototype.mul = function (arg) {
      return accMul(arg, this);
    };Copy the code

[7] Division function

    /** ** division function, used to get the exact result of division ** note: javascript division results will be error, when two floating point number division will be more obvious. This function returns a more accurate division result. ** Calls accDiv(arg1,arg2) ** returns the exact result of arg1 divided by arg2 **/
    function accDiv(arg1, arg2) {
      var t1 = 0,
        t2 = 0,
        r1, r2;
      try {
        t1 = arg1.toString().split(".") [1].length;
      } catch (e) {}
      try {
        t2 = arg2.toString().split(".") [1].length;
      } catch (e) {}
      with(Math) {
        r1 = Number(arg1.toString().replace(".".""));
        r2 = Number(arg2.toString().replace(".".""));
        return (r1 / r2) * pow(10, t2 - t1); }}// Add a div method to the Number type to make it easier to call.
    Number.prototype.div = function (arg) {
      return accDiv(this, arg);
    };Copy the code

The article is updated every week. You can search “Front-end highlights” on wechat to read it in the first time, and reply to [Books] to get 200G video materials and 30 PDF books