Home  >  Article  >  Web Front-end  >  Detailed introduction to number in JavaScript

Detailed introduction to number in JavaScript

不言
不言forward
2018-12-31 10:02:005537browse

This article brings you a detailed introduction to number in JavaScript. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Statement: Readers are required to have a certain understanding of binary

For JavaScript developers, they have more or less encountered strange phenomena in js processing numbers, such as :

> 0.1 + 0.2
0.30000000000000004
> 0.1 + 1 - 1
0.10000000000000009
> 0.1 * 0.2
0.020000000000000004
> Math.pow(2, 53)
9007199254740992
> Math.pow(2, 53) + 1
9007199254740992
> Math.pow(2, 53) + 3
9007199254740996

If you want to figure out why these strange phenomena occur, you must first figure out how JavaScript encodes numbers.

1. How does JavaScript encode numbers

Numbers in JavaScript, whether they are integers, decimals, fractions, positive or negative numbers, are all floating point numbers. They are all stored in 8 bytes (64 bits).

A number (such as 12, 0.12, -999) occupies 8 bytes (64 bits) in memory, storage method As follows:

  1. 0 - 51: Fraction part (52 bits)

  2. ##52 - 62: Exponent part (11 bits)

  3. 63: Sign bit (1 bit: 0 means the number is positive, 1 means the number is negative)

The sign bit is easy to understand. It is used to indicate whether a positive number or a negative number. There is only 1 bit and two situations (0 means positive number, 1 means negative number).

The other two parts are the fraction part and the exponent part, which are used to calculate the absolute value of a number.

1.1 Absolute value calculation formula

1: abs = 1.f * 2 ^ (e - 1023)             0 < e < 2047
2: abs = 0.f * 2 ^ (e - 1022)             e = 0, f > 0
3: abs = 0                                e = 0, f = 0
4: abs = NaN                              e = 2047, f > 0
5: abs = ∞ (infinity, 无穷大)              e = 2047, f = 0
Description:

  • This formula is a binary algorithm formula, and the result is expressed in

    abs is represented, the fractional part is represented by f, and the exponent part is represented by e

  • 2 ^ (e - 1023) represents the e - 1023 power of 2

  • because the fractional part occupies 52 digits, so the

    f The value range is 00...00 (48 0s are omitted in the middle) to 11...11 (48 1s are omitted in the middle)

  • Because the exponent part occupies 11 digits, the value range of

    e is 0(00000000000) to 2047( 11111111111)

As can be seen from the above formula:

  • 1 Storage method: 1.00 * 2 ^ (1023 - 1023) (f = 0000..., e = 1023, ... represents 48 zeros)

  • 2 storage method: 1.00 * 2 ^ (1024 - 1023) (f = 0000..., e = 1024 , ... represents 48 0s)

  • ##9

    storage method: 1.01 * 2 ^ (1025 - 1023) (f = 0100..., e = 1025, ... represents 48 zeros)

  • Storage method of 0.5

    : 1.00 * 2 ^ (1022 - 1023) (f = 0000..., e = 1022, ... Represents 48 0s)

  • 0.625

    storage method: 1.01 * 2 ^ (1021 - 1023) (f = 0100 ..., e = 1021, ... represents 48 zeros)

  • 1.2 The range and boundary of absolute values

It can be seen from the above formula:

1.2.1

0 < e < 2047

When

0 < e < 2047# When ##, the value range is:

f = 0, e = 1 to f = 11...11, e = 2046 (48 1s are omitted in the middle) That is: Math.pow(2, -1022)

to

~= Math.pow(2, 1024) - 1 (~= means approximately Equal to) Among them, ~= Math.pow(2, 1024) - 1

is the value of

Number.MAX_VALUE, so js The maximum value that can be represented. 1.2.2 e = 0, f > 0

When e = 0, f > 0

, the value range is :

f = 00...01, e = 0 (48 0s are omitted in the middle) to f = 11...11, e = 0 (48 1s are omitted in the middle) That is: Math.pow(2, -1074)

to

~= Math.pow(2, -1022)(~= means approximately equal to) Among them, Math.pow(2, -1074)

is the value of

Number.MIN_VALUE, which js can The minimum value represented (absolute value). 1.2.3 e = 0, f = 0

only represents a value 0

, but with the sign bit, so there is

0 and -0. But in the operation:

> +0 === -0
true
1.2.4

e = 2047, f > 0

This only represents one value NaN

.

But in the operation:

> NaN == NaN
false

> NaN === NaN
false
1.2.5

e = 2047, f = 0

This only represents one value

(infinity, infinity).

In operation:

> Infinity === Infinity
true

> -Infinity === -Infinity
true
1.3 The maximum safe value of absolute value

As can be seen from the above, the maximum value that can be stored in 8 bytes is

Number The value of .MAX_VALUE

is

~= Math.pow(2, 1024) - 1. But this value is not safe: the numbers from 1

to

Number.MAX_VALUE are not continuous, but discrete.

比如:Number.MAX_VALUE - 1, Number.MAX_VALUE - 2 等数值都无法用公式得出,就存储不了。

所以这里引出了最大安全值 Number.MAX_SAFE_INTEGER,也就是从 1Number.MAX_SAFE_INTEGER 中间的数字都是连续的,处在这个范围内的数值计算都是安全的。

f = 11...11, e = 1075(中间省略 48 个 1)时,取得这个值 111...11(中间省略 48 个 1),即 Math.pow(2, 53) - 1

大于 Number.MAX_SAFE_INTEGER:Math.pow(2, 53) - 1 的数值都是离散的。

比如:Math.pow(2, 53) + 1, Math.pow(2, 53) + 3 不能用公式得出,无法存储在内存中。

所以才会有文章开头的现象:

> Math.pow(2, 53)
9007199254740992

> Math.pow(2, 53) + 1
9007199254740992

> Math.pow(2, 53) + 3
9007199254740996

因为 Math.pow(2, 53) + 1 不能用公式得出,就无法存储在内存中,所以只有取最靠近这个数的、能够用公式得出的其他数,Math.pow(2, 53),然后存储在内存中,这就是失真,即不安全。

1.4 小数的存储方式与计算

小数中,除了满足 m / (2 ^ n)m, n 都是整数)的小数可以用完整的 2 进制表示之外,其他的都不能用完整的 2 进制表示,只能无限的逼近一个 2 进制小数。

(注:[2] 表示二进制,^ 表示 N 次方)

0.5 = 1 / 2 = [2]0.1
0.875 = 7 / 8 = 1 / 2 + 1 / 4 + 1 / 8 = [2]0.111
# 0.3 的逼近

0.25 ([2]0.01) < 0.3 < 0.5 ([2]0.10)

0.296875 ([2]0.0100110) < 0.3 < 0.3046875 ([2]0.0100111)
 
0.2998046875 ([2]0.01001100110) < 0.3 < 0.30029296875 ([2]0.01001100111)

... 根据公式计算,直到把分数部分的 52 位填满,然后取最靠近的数

0.3 的存储方式:[2]0.010011001100110011001100110011001100110011001100110011

(f = 0011001100110011001100110011001100110011001100110011, e = 1021)

从上面可以看出,小数中大部分都只是近似值,只有少部分是真实值,所以只有这少部分的值(满足 m / (2 ^ n) 的小数)可以直接比较大小,其他的都不能直接比较。

> 0.5 + 0.125 === 0.625
true

> 0.1 + 0.2 === 0.3
false

为了安全的比较两个小数,引入 Number.EPSILON [Math.pow(2, -52)] 来比较浮点数。

> Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON
true

1.5 小数最大保留位数

js 从内存中读取一个数时,最大保留 17 位有效数字。

> 0.010011001100110011001100110011001100110011001100110011
0.30000000000000000
0.3
> 0.010011001100110011001100110011001100110011001100110010
0.29999999999999993
> 0.010011001100110011001100110011001100110011001100110100
0.30000000000000004
> 0.0000010100011110101110000101000111101011100001010001111100
0.020000000000000004

2. Number 对象中的常量

2.1 Number.EPSILON

表示 1 与 Number 可表示的大于 1 的最小的浮点数之间的差值。

Math.pow(2, -52)

用于浮点数之间安全的比较大小。

2.2 Number.MAX_SAFE_INTEGER

绝对值的最大安全值。

Math.pow(2, 53) - 1

2.3 Number.MAX_VALUE

js 所能表示的最大数值(8 个字节能存储的最大数值)。

~= Math.pow(2, 1024) - 1

2.4 Number.MIN_SAFE_INTEGER

最小安全值(包括符号)。

-(Math.pow(2, 53) - 1)

2.5 Number.MIN_VALUE

js 所能表示的最小数值(绝对值)。

Math.pow(2, -1074)

2.6 Number.NEGATIVE_INFINITY

负无穷大。

-Infinity

2.7 Number.POSITIVE_INFINITY

正无穷大。

+Infinity

2.8 Number.NaN

非数字。

3. 寻找奇怪现象的原因

3.1 为什么 0.1 + 0.2 结果是 0.30000000000000004

0.3 的逼近算法类似。

0.1 的存储方式:[2]0.00011001100110011001100110011001100110011001100110011010

(f = 1001100110011001100110011001100110011001100110011010, e = 1019)
0.2 的存储方式:[2]0.0011001100110011001100110011001100110011001100110011010
(f = 1001100110011001100110011001100110011001100110011010, e = 1020)
0.1 + 0.2: 0.0100110011001100110011001100110011001100110011001100111
(f = 00110011001100110011001100110011001100110011001100111, e = 1021)

f = 00110011001100110011001100110011001100110011001100111 有 53 位,超过了正常的 52 位,无法存储,所以取最近的数:

0.1 + 0.2: 0.010011001100110011001100110011001100110011001100110100
(f = 0011001100110011001100110011001100110011001100110100, e = 1021)

js 读取这个数字为 0.30000000000000004

3.2 为什么 Math.pow(2, 53) + 1 结果是 Math.pow(2, 53)

因为 Math.pow(2, 53) + 1 不能用公式得出,无法存储在内存中,所以只有取最靠近这个数的、能够用公式得出的其他数。

比这个数小的、最靠近的数:

Math.pow(2, 53)
(f = 0000000000000000000000000000000000000000000000000000, e = 1076)

比这个数大的、最靠近的数:

Math.pow(2, 53) + 2
(f = 0000000000000000000000000000000000000000000000000001, e = 1076)

取第一个数:Math.pow(2, 53)

所以:

> Math.pow(2, 53) + 1 === Math.pow(2, 53)
true

The above is the detailed content of Detailed introduction to number in JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete