This article is published by the Cloud + community

Console. log(0.1+0.2===0.3)// false. In JavaScript, all numbers including integers and decimals are represented as the Number type. This paper introduces the binary storage standard of Number to understand the precision of floating point Number operation, and understand how to value the MAX_VALUE and other attribute values of Number object. Finally, it introduces some common solutions of floating point Number precision operation.

Number storage standard

JavaScript numbers are represented by 64-bit double precision floating-point types defined by IEEE 754. For an example of byte allocation, take a look at this figure from Wikipedia:

As you can see from the figure above, from high to low, the 64 bits are divided into 3 segments:

  • Sign: sign bit, occupying 1 bit;
  • 1. Exponent: 11 digits;
  • Fraction: indicates a valid number of 52 digits.

The index bit has 11 bits and ranges from 0 to 2047. When the exponential bit e=0 or e=2017, it has different special meanings according to whether the significant digit bit F is 0, as shown in the following table:

For the normal number, the value of the index bit is offset by -1023 to facilitate the representation of the negative exponent. For a non-zero number, the first significant digit in binary scientific notation is always 1. Thus, the value of a double floating-point number is

For a subnormal number, it can be used to represent numbers closer to 0. It is special because the significant digits are preceded by 0 instead of 1, and the exponent is offset by -1022, so the value is:

The value of several properties in the Number object

Once you know how Number is stored, it becomes clear how the properties of the Number object are evaluated.

Number.MAX_VALUE: specifies the maximum Number that can be represented. Obviously, the maximum Number that can be represented when e and f are both maximized is

Number.MIN_VALUE: The smallest positive Number that can be represented, represented by the smallest subnormal Number. When e = 0, the last bit of f is 1, and the other bits are 0

Number.EPSILON: Represents the difference between 1 and the smallest floating point Number that Number can represent greater than 1. A value of

Number.MAXSAFEINTEGER: represents the largest safe integer in JavaScript. Integers that can be represented continuously and precisely are safe integers. For example, 2^54 is not a safe integer because it is the same as 2^54+1, e=1077,f=0. Math.h pow (2,) = = = math.h pow (2,) + 1 / / true. Integer is converted into binary, there is no Numbers after the decimal point, and expressed in scientific notation of binary, decimal point up to 52, plus a pre – 1, there are 53 Numbers, so when a number to convert binary, if figures more than 53, is bound to be truncated at the end of the part, which lead to can’t accurate said, Is an unsafe integer. So the smallest integer that can be truncated is 100… 001=2^53+1 (with 52 zeros). If we set this number to X, then any integer less than X can be represented exactly, plus the continuous condition, so X minus 1 is not the answer, X minus 2 is. Number.MAX_SAFE_INTEGER The final value is

Number.MINSAFEINTEGER: indicates the smallest secure integer in JavaScript. The value is -9007199254740991 and the value is a negative value for number. MAX_SAFE_INTEGER

Why does 0.1+0.2 not equal 0.3

Now look at console.log(0.1+0.2===0.3)// false, the number 0.1 converted to binary is 0.0001100110011… The 1.10011001… 1001 * 2^-4 (there are 52 decimal places, i.e. 13 1001 cycles). Since the 53rd bit is 1, similar to rounding off base 10, and binary is “rounded off by zero”, the final binary scientific notation for 0.1 is 1.10011001… 1010 * 2^-4, the binary numeric size is actually 0.000110011001… 10011010. The following code verifies this value (printed with the last 0 removed) :

var a = 0.1;console.log(a.toString(2)); / / 0.0001100110011001100110011001100110011001100110011001101
Copy the code

Similarly, the final value of the decimal number 0.2 converted to binary is 1.10011001… 1010 * 2^-3 = 0.00110011… 100111010; Decimal 0.3 converted bits The final value of binary is 1.00110011… * 2 ^ 0011-2

var b = 0.2;console.log(b.toString(2)); / / var c = 0.001100110011001100110011001100110011001100110011001101 0.3; console.log(c.toString(2)); / / 0.010011001100110011001100110011001100110011001100110011
Copy the code

Thus, the value of 0.1+0.2 is the sum of the corresponding binary values of 0.1 and 0.2 above, as shown in the figure below

In the figure above, for the sum, the “zero rounding” with 52 significant decimal places is the final value: 0.01001100… 110100 (bit 53 is 1, so it is advanced by 1), as shown in the code below. This value is significantly different from the final binary representation of 0.3 above, which explains the root reason why 0.1 + 0.2 is not equal to 0.3 (in fact, this value translates to the decimal constraint of 0.30000000000000004). Note: The printed length is 54, because there are 52 significant decimal places, preceded by ‘0.01’, the length is 4, and the last two zeros are removed, so the printed length is 52+4-2 = 54.

var d = 0.1 + 0.2;console.log(d.toString(2)); / / 0.0100110011001100110011001100110011001100110011001101 console. The log (which oString (2) length); / / 54
Copy the code

Floating point number precision operation solutions

Different scenarios can have different solutions to the problem of js floating point arithmetic precision loss. 1. If you are simply using the result of a floating point Number, you can borrow the toFixed and parseFloat methods of the Number object. In the code snippet below, the fixed parameter indicates the number of decimal places to keep, and the precision can be adjusted according to the actual scenario.

function formatNum(num, fixed = 10) {    return parseFloat(a.toFixed(fixed))}var a = 0.1 + 0.2;console.log(formatNum(a)); / / 0.3
Copy the code

2. If floating point numbers need to be added, subtracted, multiplied, and divided, we know from the above that integers in the range less than number. MAXSAFEINTEGER can be accurately represented, so we can first convert the decimal into integers, and then convert the result into the corresponding decimal. For example, the addition of two floating point numbers:

 function add(num1, num2) {  var decimalLen1 = (num1.toString().split('. ') [1] | |' ').length; / / the first parameter, the decimal number var decimalLen2 = (num2. The toString (). The split ('. ') [1] | | "). The length; Var baseNum = math.pow (10, math.max (decimalLen1, decimalLen2)); return (num1 * baseNum + num2 * baseNum) / baseNum; } the console. The log (add (0.1, 0.2)); / / 0.3
Copy the code

The resources

  • En.wikipedia.org/wiki/IEEE_7…
  • En.wikipedia.org/wiki/Double…
  • En.wikipedia.org/wiki/Normal…
  • En.wikipedia.org/wiki/Denorm…

This article has been published by Tencent Cloud + community authorized by the author