The reason for writing this note is that I am curious about the accuracy of JS. I started from reference 3, but FRANKLY I didn’t understand it very well. I looked up all kinds of information by following the steps, and solved my doubts one ring by one ring. In the process remembered the safe integer again, this kind of problem all because of the precision produces, so according to my understanding ability string next knowledge chain… , only as this study record (is drop, only send me a few look), if there is a mistake welcome correction (in case was seen by the words…

1. Pre-knowledge

1.1 B and B

B = Byte = Byte = data storage unit B = Bit = Bit/Bit = Data transmission unit 1 Byte = 8 bitsCopy the code

Data is stored in units of “bytes”, and most data is transferred in units of “bits”. A bit represents a 0 or a 1 (binary), and every eight bits (abbreviated b) form a Byte (abbreviated B). Is the smallest level of information unit double precision 8 bytes 64 bits (1 byte = 8 bits) — Baidu Encyclopedia

1.2 Values represented by binary

For example, 1 byte 8 bits Indicates the signed bit. 0 indicates an integer, and 1 indicates a negative number. Range: 11111111 to 10000000 = 00000000 to 01111111 (binary) -127 to -0 = +0 to 127 (decimal) Unsigned range: 00000000 to 11111111 (binary) 0 to 255 (decimal)Copy the code

Conclusion:

Range of values represented by n bits:

Signed: [-2n-1 + 1, 2n-1-1]

Unsigned: [0, 2n-1]

// A simple way to get the maximum valueN is the digit number with the sign [-Math.pow(2, n-1) + 1, + Math.pow(2, n-1) - 1] unsigned [0.Math.pow(2, n) -1]

// Simple binary to decimal, integer part methods are provided here
// binaryStr Indicates a valid binary string, and sign indicates the symbol bit
function binary2decimal(binaryStr, sign) {
    let len = binaryStr.length;
    if(! len)return binaryStr;
    let flag = 1;
    if (sign) {
        flag = Math.pow(-1, +binaryStr[0]);
        binaryStr = binaryStr.slice(1);
        len -= 1;
    }
    const res = String(binaryStr).split(' ').reduce((pre, cur, inx) = > pre + cur * Math.pow(2, len - inx - 1), 0);
    return res * flag;
}
Copy the code

1.3 Accuracy in IEEE 754

Double = 8 bytes = 64 bits, such as C++ long long, double, JS Number... Single precision = 4 bytes = 32 bits, as in C++ long int...Copy the code

IEEE 754 floating point number storage format


Value = sign x exponent x fraction \displaystyle {\texttt {Value}}={\texttt {sign}}\times {\texttt {exponent}}\times {\texttt {fraction}}

The value of a floating point number is equal to the sign bit times the exponent bias times the fraction.

Single precision

Single precision is 4 bytes 32 bits, 1 sign bit, 8 exponent bits, 23 mantissa bits, a total of 32 bits.

Storage form corresponding calculation formula is: V = (1 -) S * (1 F) * 2 E – 127 – V = (1) ^ S \ times (1 F) \ times ^ 2 = {} E – 127 V (1) – S * (1 F) * 2 E – 127

S/sign: symbol, 1 bit E/exponent: 8 bits, E range (0, 255). Since it is an unsigned integer, the range is [0, 255], denoting the actual exponent e= e-127. 127 is the offset, calculated as "2^ E -2"! The range of e should be [-127, 127], but the IEEE 754 specification says that "-127 and 128 are used for special value processing ", so the actual range of e is [-126, 127] and e is (0, 255). Fraction: the mantissa/decimal part, 23 digits, (1Copy the code
double

Double precision is 8 bytes 64-bit, sign bit 1, index bit 11, mantissa 52, a total of 64 bits.

Storage form corresponding calculation formula is: V = (1 -) S * (1 F) * 2 E – 1023 – V = (1) ^ S \ times (1 F) \ times ^ 2 = {} E – 1023 V (1) – S * (1 F) * 2 E – 1023

S/sign: symbol, 1 bit E/exponent: 11 bits, E range is (0, 2047). Same as above, e= e-1023, the range of E [-1022, 1023]. F/fraction: mantissa/decimal, 52 digitsCopy the code

2. Precision in JS

JavaScript’s Number type is a double precision IEEE 754 64-bit floating-point type

So 1 to 1.0 is completely equal, storage for 0 * (1.0) * (1 -) (1) ^ 0\20 times (1.0) \ times 2 ^ 0 (0-1) * (1.0) * 20, S = 0, E – 1023 = 0, F = 0. The final storage format is as follows

   0 01111111111 00000000000000000000000000000000000000000000000000000
   S E           F
Copy the code

There are 52 mantissa digits, but 53 significant digits. The default value is 1≤ significant digit ≤2, so the integer part is fixed to 1, accounting for 1 bit. Do not forget to count the integer part when converting binary to decimal.

3. Conversion between binary and decimal

Method 1: Online tools

Use base conversion web tools

Method 2: JS native method

Decimal to binary
function decimal2binary(n) {
    return Number(n).toString(2);
}
Copy the code

Binary to decimal (integer part)
function binary2decimal(binaryStr) {
    return parseInt(binaryStr, 2);
}
Copy the code

Method three: base conversion algorithm

(Directions 👉 Reference materials 4 blog: JS decimal and binary conversion

Binary to decimal

When the whole and decimal parts are converted to decimal and combined, each place is multiplied by a number of powers of 2, calculated by the distance to the first place of the decimal point, taking the positive left and the negative right. For example, 10.101, put 10101 in an array with an index of 1 before the decimal point, 1 * 21 21 + 0-0-1 + 1 * 21 21-2 + 0-3 + 1 x 21-41 \ times 2 ^ {1-0} + 0 \ times 2 ^ {1} 1 + 1 \ times 2 ^ {2} + 0 \ times ^ 2 + 1 \ times {1-3} 2 ^ 1 x 21 {} 1-4-0 + 0 * 21-21-1 + 1 + 2 0 * 21 21-3 + 1-4

// A simple method for converting a floating point number from binary to decimal is provided here
function binary2decimal(binaryStr) {
  const point = binaryStr.indexOf('. ');
  if (point === 0) binaryStr = '0' + binaryStr;
  const base = point === -1 ? binaryStr.length - 2 : point - 1;
  return (binaryStr.substring(0, point) + binaryStr.substring(point + 1)).split(' ').reduce((pre, cur, index) = > pre + cur * Math.pow(2, base - index), 0);
}
binary2decimal('1111011.111'); / / 123.875
Copy the code
Decimal to binary
The integer part

Method: Mod 2 and reverse order

The decimal part

Method: multiply by 2 to take the whole, order

In 0.1, for example

0.1 * 2 = 0.2 > > > 0 0.2 * 2 = 0.4 > > > 0 / / 0.4 * 2 = 0.8 > > > 0 0.8 * 2 = 1.6 > > > 1, 0.6 * 2 = 1.2 > > > 1, 0.2 * 2 = 0.4 > > > 0 // Start repeating so 0011 is a loop sectionCopy the code

(decimal) 0.1 = (binary) 0.0(0011) loop

4. Why is the accuracy lost

Finally to the main body… /doge

(Directions 👉 Reference 3 blog post: interview essential detail JS digital precision

0.1 = 0.0 circulation material 0.0 (0011) 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 001 missing after (52) = 0.09999999999999987 indicates 0.1

So the conclusion is: the essence of the reason is the decimal conversion to binary, there is an infinite loop, storage only 52 bits and loss of accuracy

5. Safe integers

(Directions 👉 Resources 7 JavaScript floating point traps and solutions

An integer that corresponds to a floating point number


  1. 2 53 1 2 ^ {53} – 1
  2. Number.MAX_SAFE_INTEGER

★,°:.☆( ̄▽ ̄)/$:.°★.

(The first time to write, probably only their own can understand… I’ll leave it there and I’ll fill it out later

Todolist: 1. Working principle of toString 2. Method of accuracy in Number toFixed, toPrecision, etc. 3Copy the code

The resources

  1. Wikis: IEEE 754 floating-point storage form
  2. Blog post (easy to understand, 1 subject) : IEEE754 standard format
  3. JS digital accuracy
  4. Blog post: JS decimal and binary conversion
  5. Zhihu: the conversion between binary and decimal
  6. Blog post: floating point binary to decimal
  7. Issue: JavaScript floating point traps and solutions
  8. Zhihu: Safe integer