# How numbers are stored in javascript?

• 12 mins read

## 1. Fundamental

A computer is an electronic device, which is composed of many circuits inside it. Each circuit has only 2 states: ON and OFF, or 0 and 1, so it can only store binary values. As a result, computers can only store and process sequences of 0 and 1. So all kinds of data has to be converted into binary number before computers can understand them.

The value 0 and 1 are the smallest units of data. They are called bits. 8 bits combine to a byte and 4 bytes combine to a word.

Now, if you don't know how to convert decimal numbers to binary numbers, take a look at this video and do some exercises before continuing.

There are 3 common ways to represent a block of data, which are binary, octal, and hexadecimal. Let's try to represent number 129 in the 3 different ways.

First we convert number 129 from decimal to binary, which is `10000001`

.

So 129 is represented as `10000001`

in binary.

An octal number can represent number from `0`

to `7`

, which is from `000`

to `111`

. So an octal can
represent 3 binary digits. To represent `10000001`

, which is 8 binary digits, we need 3 octal
numbers. `10000001`

is now splited into 3 parts `010`

`000`

`001`

. After that, we convert each part
to octal number, which is `2`

`0`

`1`

. To distinguish it with decimal number, we prefix it with
leading `0`

. So 129 is represented as `0201`

in octal.

Hexadecimal number is similar to octal number, but it can represent number from `0`

to `15`

, which
is from `0000`

to `1111`

. So an hexadecimal can represent 4 binary digits. Now we're splitting the
number `10000001`

into 2 parts of 4 bits, which is `1000`

and `0001`

. Then convert each part to
hexadecimal, we have the value of `81`

. To distinguish it with decimal number, we prefix it with
`0x`

. So 129 is represented `0x81`

in hexadecimal.

Next we're gonna see how each type of data: integer, rational, and irrational number are stored in memory.

## 2. Integer

### 2.1 Unsigned Integer

Unsigned integers are generally stored as simple binary numbers. For example, `1`

is stored as `1`

,
`2`

is stored as `10`

, and `129`

is stored as `10000001`

.

### 2.2 Signed Integer

There are 3 most common formats to store signed integer numbers.

#### a. Signed Magnitude

Signed Magnitude format uses the left most bit as the sign indication, and treat the remaining bit as they represent unsigned integer.

The convention for the left most bit (most significant bit): `0`

for positive, `1`

for negative. For
example: `100111`

represent `-7`

.

The problem with this system is that it does not support binary arithmetic, which is the most important for computers. For example:

```
100111 // -7
000111 // 7
100111 + 000111 = 101110 // -14, which is not 0 as we expected.
```

#### b. One's Complement

One's Complement of a binary number is just another binary number that when we add it to the original number, the result is a binary number with all bits are 1. To obtain binary number, you can simply flip all the bits.

For example, `000111`

is `7`

, then it's one's complement is `111000`

, which is `56`

. You can notice
that `56 + 7 = 63 = 2^6-1`

. So one's component of a number is the result of the max value that can
be represented with number of bits minus that number itself. With this system, we also have the
first bit as the sign indication.

To see which value a negative binary number represents, we just find it's one's complement. For
example: `100011`

is a negative number, it's one's complement is `011100`

, which is `28`

. So
`100011`

is `-28`

.

We have two 0 values in this system. `000000`

which is `+0`

and `111111`

which is `-0`

.

In this system, the binary arithmetic is partially solved. If we add `12`

and `-12`

, we get `0`

as
expected. `12`

is `001100`

, it's one's complement is `110011`

, when we add these 2 values, we get
`111111`

, which is -0 as expected.

But there are still some special cases: For example, add `3`

to `-2`

.

```
3 // 000011,
-2 // 111101,
000011 + 111101 = 000000 // which is +0, not 1 as we expected.
```

In this case, we need to add the leftmost (the carry, which is `000001`

) to the result. After adding
the carry, we got `000001`

which is 1 as expected.

Let's try another example, add `10`

to `-4`

.

```
10 // 001010
-4 // 111011
001010 + 111011 = 000101 // 5
000101 + 000001 = 000110 // add the carry
```

After adding the carry, we got 6 as expected.

#### c. Two's Complement

Contrary with one's complement, a two's complement of a number is another number that when we add it to the original number, we get a number that all bits are 0. To find the two's complement of a number, we first find its one's complement then add 1 to it.

For example, we have `7`

which is `000111`

, it's one's complement is `111000`

, then it's two's
complement is `111000 + 1 = 111001`

. When we add a number and it's two's complement, we get a number
which all bits are 0. In this example, `000111 + 111001 = 000000`

.

We still use the first bit as sign indicator. To find which value a negative number represents, we just find it's two's complement.

For example, we have a number `100111`

, it's one's complement is `011000`

, it's two's complement is
`011001`

, which is 25. Then the original number is -25.

In this system, we have only one representation of 0, which is `000000`

. and the value of `111111`

is `-1`

. and `100000`

is `-32`

. Now with 6 bits, we can represent values from `-32`

to `31`

. With
signed magnitude and one's complement, we can only have values from `-31`

to `31`

. Moreover, the
binary arithmetic is all solved in two's complement.

### 2.3 Conclusion

Integer is usually store in 16 bits, 32 bits or 64 bits, depending on how big the number is. They
are often called `short`

, `int`

, and `long`

type respectively.

Unsigned integers are stored as simple binary numbers.

*Signed integers are often stored as 2's complement so that it can store maximum number of values
and leverage arithmetic calculation.*

## 3. Rational number

### 3.1 What is floating point number?

There are 3 common ways to represent a rational number.

- Decimal representation, such as:
`12.34`

,`-0.6`

,`123.7777`

- Fractional representation, such as:
`1/2`

,`5/6`

,`10/3`

- Floating points number, such as:
`1.23*10^5`

,`33.45*10^6`

Floating point number is a format to represent rational number with the point (.) can be moved (floating).

### 3.2 How floating point number is stored?

#### a. Scientific notation

First, we have to know how to represent a number in scientific notation. It's simple. We rewrite the
number in 2 parts. The first part is just the digits with the point right after the first digit.
Then multiply it with a number that put the point where it should be. Then rewrite that number in
format `base^x`

, which is the second part.

For example, let's write 12345 in scientific notation.

```
12345 = 1.2345 x 10.000 // so the first part is 1.2345
// decimal number -> the base is 10
10.000 = 10^4 // so the second part is 10^4
=> 1.2345 * 10^4 is scientific notation
```

Do the same for `101.101`

, which is a binary number, we have `1.01101 * 2^2`

as its scientific
notation.

#### b. IEEE 754

Nearly all hardwares and programming languages use floating point number in the same binary format, which is specified in IEEE Standard for Floating-Point Arithmetic (IEEE 754). Basically, it normalizes the number to the scientific notation that we already learn above, then use a binary number to represent that normalized number.

There are 2 most used formats that are 32 bit (float) and 64 bit (double). They are divided into 3
parts: *sign bit*, *exponent* and *significand* (or *mantissa* ).

Let's use the number `101.101`

as our example. We normalize it to scientific notation first

```
101.101 = 1.01101x2^2
```

a. * sign bit*: The leftmost, or the most significant bit (MSB) is used to indicate the sign of
the number, 0 is positive and 1 is negative. In our case, it's a positive number so the sign bit
is 0.

b. * the exponent*: use to indicate the distance between the binary point of the original number
and the binary point of the normalized number.

```
101.101
-.12---
1.01101
```

In our example, the normalized number is `1.01101x2^2`

so the exponent is 2.

To represent both negative and positive exponent, we use a bias to add to the actual exponent. For
example: if we use 6 bit to represent number, then we can represent only unsigned number from
`000000`

to `111111`

, which is from `0`

to `63`

. But we want to represent negative numbers too, so
we find a bias following the IEEE 754 formula.

```
bias = 2^(n - 1) - 1 = 2^(6 - 1) - 1 = 31
```

Then we can represent number from `(0-31)`

to `(63-31)`

, which is `-31`

to `32`

.

In the above number, the actual value of the exponent that is stored is `2 + 31 = 33`

, which is
`100001`

. In other words, to store the number 2 as exponent, we don't use `000010`

but `100001`

.

c. * The mantissa*: because in the normalized form (scientific notation form), the integer part
is always 1, so we only have to store the fraction part. That fraction part (the digits after the
point) is call the mantissa. In the above number:

`1.01101x2^2`

, the mantissa we need to store is
`01101`

.So the number `101.101`

(which is `5.625`

in decimal) will be represent as the following using
floating point 32 bit:

0 | 1000 0001 | 0110 1000 0000 0000 0000 000 |

sign |
exponent |
mantissa |

- number is positive → bit sign is 0
- next 8 bit is for exponent:
`2^2`

, the exponent is`2`

, then we add it to the bias, which is`127`

for 8 bit then we get`129`

→ exponent is`1000 0001`

- last 23 bits is for mantissa, as we talk above, the mantissa is
`01101`

Floating point numbers using 32 bits are called `single-precision`

or `single`

. Floating point
numbers using 64 bits are called `double-precision`

or `double`

. It has 3 parts similar to `single`

but it uses different numbers of bits to store the sign bit, exponent, and mantissa.

sign | exponent | mantissa | |
---|---|---|---|

single |
1 bit | 8 bits | 23 bits |

double |
1 bit | 11 bits | 52 bits |

### 3.3 Why the exponent use bias, not one's complement or two's complement

If we use 1's complement or 2's complement, it will be hard for comparison of numbers.

For example, we want to compare the number 5.625 above with 5. we will have the representation of 5 in 1's complement and 2's complement and in biased

1's complement: | 0 | 1111 1101 | 0100 0000 0000 0000 0000 000 |

2's complement: | 0 | 1111 1110 | 0100 0000 0000 0000 0000 000 |

biased: | 0 | 1000 0001 | 0100 0000 0000 0000 0000 000 |

When looking at 1's complement and 2's complement exponent numbers and compare it to 5.625 above, we can not determine which number is larger without additional computation. On the other hand, with biased exponent, we can easily compare bit by bit from left to right to determine which number is larger. This is faster for computer to compute.

## 4. Irrational Number

In general, computers cannot store or manipulate all irrational numbers. Most irrational numbers are not computable, and therefore can't be handled or evaluated at all by a computer. Since we cannot compute such numbers, there is never any need to represent them.

## 5. How numbers are stored in javascript?

Javascript have only one type for numeric values, which is `number`

. There are no different types
for integer and real number in javascript. Under the hood, all numeric numbers in javascript are
store as double-precision (64 bits) floating-point number, which is described in the section number
3.2b above.

Although Javascript uses 2's complement format (which is described in section 2.2c) to store integer value in order to do some arithmetic operation such as bitwise operation, you should keep in mind that in general, numbers in javascript are floating point number. Because it will sure make you confused sometimes.

```
0.1 + 0.2; // 0.30000000000000004
```

Floating-point arithmetic can only produce approximate results, rounding to the nearest representable real number. When you perform a sequence of calculations, these rounding errors can accumulate, leading to less and less accurate results. Rounding also causes surprising deviations from the kind of properties we usually expect of arithmetic. For example, real numbers are associative, meaning that for any real numbers x, y, and z, it’s always the case that (x + y) + z = x + (y + z). But this is not always true of floating-point numbers:

```
(0.1 + 0.2) + 0.3; // 0.6000000000000001
0.1 + (0.2 + 0.3); // 0.6
```

Floating-point numbers offer a trade-off between accuracy and performance. When accuracy matters, it's critical to be aware of their limitations.

One useful workaround is to work with integer values wherever possible, since they can be represented without rounding. When doing calculations with money, programmers often scale numbers up to work with the currency’s smallest denomination so that they can compute with whole numbers. For example, if the above calculation were measured in dollars, we could work with whole numbers of cents instead:

```
(10 + 20) + 30; // 60
10 + (20 + 30); // 60
```

## 6. Summary

In short, in javascript, all numbers are 64 bits floating point numbers. After reading this post, I hope you can:

- Understand how computer stores numbers in general.
- Understand how javascript stores its numeric data.
- Be always aware of limitations of precisions in floating point arithmetic when working with javascript.

## 7. Sources and related reading

- Item 2: "Understand JavaScript’s Floating-Point Numbers" of Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript by David Herman.
- Computer Memory
- How computers represent negative binary numbers?