# Why Are The Numbers In JavaScript So Strange?

*·*- 30 Jan 2019
*·**·*- 3304 Views

Note: The knowledge of binary is needed to read this article.

Many JavaScript developers may have come across the strange phenomena of dealing with the numbers in JavaScript, 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 explore the reason, first of all, you need to figure out how numbers are encoded in JavaScript.

## 1. How Numbers Are Encoded In JavaScript?

The numbers in JavaScript, no matter they are integers, fractions, or positive or negative numbers, are all float numbers, which are stored in a binary format, in 8 bytes (64 bits).

A number (such as `12`

, `0.12`

, `-999`

) occupies 8 bytes (64 bits) in memory and is stored in the way as follows:

`0 - 51`

: fraction part (52 bits)`52 - 62`

: exponent part (11 bits)`63`

: sign bit (1 bit: 0 means the number is positive, 1 means the number is negative)

The sign bit is easy to understand and is used to indicate whether the number is positive or negative. And it has only 1 bit for two cases (0 for the positive number and 1 for the 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 Formula for calculating absolute values

```
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
```

- It is a binary calculation formula, and the result is represented with
`abs`

, the fraction part is represented with`f`

, and the exponent part is represented with`e`

;`2 ^ (e - 1023)`

represents the`e - 1023`

power of`2`

；- Since the fraction part occupies 52 bits, the value of
`f`

ranges from`00...00`

(48`0`

's are omitted in the middle) to`11...11`

( 48`1`

's are omitted in the middle)- Since the exponent part occupies 11 bits, the value of
`e`

ranges from`0`

(`00000000000`

) to`2047`

(`11111111111`

).So:

`1`

is stored as:`1.00 * 2 ^ (1023 - 1023)`

（`f = 0000..., e = 1023`

，`...`

represents 48 0's)- 2 is stored as:
`1.00 * 2 ^ (1024 - 1023)`

（`f = 0000..., e = 1024`

，`...`

represents 48 0's)- 9 is stored as:
`1.01 * 2 ^ (1025 - 1023)`

（`f = 0100..., e = 1025`

，`...`

represents 48 0's)- 0.5 is stored as:
`1.00 * 2 ^ (1022 - 1023)`

（`f = 0000..., e = 1022`

，`...`

represents 48 0's)- 0.625 is stored as:
`1.01 * 2 ^ (1021 - 1023)`

（`f = 0100..., e = 1021`

，`...`

represents 48 0's)

### 1.2 Range and boundary of absolute values

What can be seen from the above formula is:

#### 1.2.1 0 < e < 2047

When `0 < e < 2047`

, the range of values is from `f = 0, e = 1`

to `f = 11...11, e = 2046`

(48 1's are omitted in the middle)

The above means that the range is from `Math.pow(2, -1022)`

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

(`～=`

represents approximately equal)

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

is the value of `Number.MAX_VALUE`

, which is the maximum value you can represent in JavaScript.

#### 1.2.2 `e = 0, f > 0`

When `e = 0, f > 0`

, the range of values is from `f = 00...01, e = 0`

(48 0's are omitted in the middle) to `f = 11...11, e = 0`

(48 1's are omitted in the middle)

The above means that the range is from: `Math.pow(2, -1074)`

to `~= Math.pow(2, -1022)`

(`～=`

represents approximately equal)

Here, `Math.pow(2, -1074)`

is the value of `Number.MIN_VALUE`

, which is the minimum value (the absolute value) you can represent in JavaScript.

#### 1.2.3 e = 0, f = 0

It only represents the value of `0`

, but there are `+0`

and `-0`

if you take with the sign bit.

However in the operation:

```
> +0 === -0
true
```

#### 1.2.4 e = 2047, f > 0

It only represents the value of `NaN`

.

But in the operation:

```
> NaN == NaN
false
> NaN === NaN
false
```

#### 1.2.5 e = 2047, f = 0

This only represents the value of `∞`

(infinity).

In the operation:

```
> Infinity === Infinity
true
> -Infinity === -Infinity
true
```

### 1.3 Maximum Safe Value Of Absolute Values

As you can see from the above, the maximum value that can be stored in 8 bytes is the value of `Number.MAX_VALUE`

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

.

But the values are not safe: the numbers in the range from `1`

to `Number.MAX_VALUE`

are not continuous, but discrete.

For example: The values of `Number.MAX_VALUE - 1`

, `Number.MAX_VALUE - 2`

and so on can't be formulated, thus they can't be stored.

So here comes the maximum safe value, `Number.MAX_SAFE_INTEGER`

, which means that the numbers from `1`

to `Number.MAX_SAFE_INTEGER`

are continuous, so the numerical calculations in this range are safe.

You can get the value `111...11`

(48 1's are omitted in the middle), ie `Math.pow(2, 53) - 1`

when `f = 11...11, e = 1075`

(48 1's are omitted in the middle).

Values greater than `Number.MAX_SAFE_INTEGER：Math.pow(2, 53) - 1`

are discrete.

For example: `Math.pow(2, 53) + 1`

, `Math.pow(2, 53) + 3`

can't be formulated and can't be stored in memory.

So it is the reason why you feel the code at the beginning of this article is so strange:

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

`Math.pow(2, 53) + 1`

can't be formulated, so it can't be stored in memory. Thus you have to get another number which can be formulated as well as is closest. Here the obtained number is `Math.pow(2, 53)`

, and it can be stored in memory then. This leads to distortion, so it is not safe.

### 1.4 Storage And Calculation Of Decimal Fractions

Only the decimal fractions that satisfies the condition of `m / (2 ^ n)`

(`m, n`

are integers) can be represented by the binary completely. The others can't be represented by the binary completely, and only can be represented by approaching the binary fraction infinitely..

(Note: `[2]`

means binary)

```
0.5 = 1 / 2 = [2]0.1
0.875 = 7 / 8 = 1 / 2 + 1 / 4 + 1 / 8 = [2]0.111
```

Approximation of 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)
//... It'll calculate according to the formula until the 52 bits of the fractional part are filled, and then take the nearest number.
Storage for 0.3：[2]0.010011001100110011001100110011001100110011001100110011
(f = 0011001100110011001100110011001100110011001100110011, e = 1021)
```

What can be seen from the above is that most of the decimals are only approximations, and only a small part is the true value, so only the values (the decimals that satisfy the condition of `m / (2 ^ n)`

) from this small part can be compared directly, while the others can't be compared directly.

```
> 0.5 + 0.125 === 0.625
true
> 0.1 + 0.2 === 0.3
false
```

In order to compare two decimals safely, we can introduce `Number.EPSILON [Math.pow(2, -52)]`

to compare floating point numbers.

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

### 1.5 Maximum Reserved Digits For Decimals

It retains a maximum of 17 significant digits when JavaScript reads a number from memory.

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

## 2. Constants In The Number Object

### 2.1 Number.EPSILON

The difference between 1 and the smallest floating point number represented by Number and greater than 1.

`Math.pow(2, -52)`

It's used for safe comparison between floating point numbers.

### 2.2 Number.MAX_SAFE_INTEGER

The maximum safe value of the absolute value.

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

### 2.3 Number.MAX_VALUE

The maximum value that js can represent (the maximum value that can be stored in 8 bytes).

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

### 2.4 Number.MIN_SAFE_INTEGER

The minimum safe value (including the sign symbol).

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

### 2.5 Number.MIN_VALUE

The minimum value (absolute value) that js can represent.

`Math.pow(2, -1074)`

### 2.6 Number.NEGATIVE_INFINITY

Negative infinity.

`-Infinity`

### 2.7 Number.POSITIVE_INFINITY

Positive infinity.

`+Infinity`

### 2.8 Number.NaN

It's non-numeric.

## 3. The Cause Of The strange Phenomenon

### 3.1 Why the result of 0.1 + 0.2 is 0.30000000000000004

It's similar to the approximation algorithm for `0.3`

.

```
Storage for 0.1：[2]0.00011001100110011001100110011001100110011001100110011010
(f = 1001100110011001100110011001100110011001100110011010, e = 1019)
Storage for 0.2：[2]0.0011001100110011001100110011001100110011001100110011010
(f = 1001100110011001100110011001100110011001100110011010, e = 1020)
0.1 + 0.2: 0.0100110011001100110011001100110011001100110011001100111
(f = 00110011001100110011001100110011001100110011001100111, e = 1021)
```

However, there are 53 bits for `f = 00110011001100110011001100110011001100110011001100111`

, which exceeds the normal 52 bits and cannot be stored, so it takes the nearest number:

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

Thus Js reads this number as 0.30000000000000004

### 3.2 Why the result of Math.pow(2, 53) + 1 is Math.pow(2, 53)

`Math.pow(2, 53) + 1`

cannot be formulated and cannot be stored in memory, so it has to take the number that can be formulated and is closest.

The closest and smaller number:

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

The closest and bigger number:

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

Take the first number: `Math.pow(2, 53)`

.

So:

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

## 0 Comment

Login to post a comment

Login With Github