# Numbers in JavaScript + (BigInt)

**"Last updated: September 2026"**  
  
To represent numeric value in javascript we need to use numbers.  
As I mentioned in my previous article([Types Values and variables in Javascript](https://blog.ganeshjaiwal.dev/types-values-and-variables-in-javascript)) we need to use specific data types to store specific values.

In our case, if we want to store some numeric value we need to use a **number data type**.

JavaScript represents numbers using the 64-bit floating-point format defined by the IEEE 754 standard.

The JavaScript number format allows us to represent all numeric values between, **\-9,007,199,254,740,992 (-2⁵³)** and **9,007,199,254,740,992 (2⁵³)**.  
If we use values larger than this we may lose precision in trailing digits.

If a number appears directly in a JavaScript program, it called numeric literals.  
JavaScript supports numeric literals in several formats.  
Let’s look into it one by one.

### Integer Literals

In the JavaScript program, we can use the sequence on digits from 0 to 9, to represent any base-10 numeric values.

Examples:-

```plaintext
5
88
56
555986547
```

JavaScript also allows us to use hexadecimal values(base-16). Hexadecimal literals are represented by adding **0x or 0X** as a prefix to that number.  
It uses **0 to 9 or a(or A) to f(or F)** witch represents values from 10 to 15.

Examples:-

```plaintext
0xfca99       // => 1034905 = (15 × 16⁴) + (12 × 16³) + (10 × 16²) + (9 × 16¹) + (9 × 16⁰)
8873          // => 34931 = (8 × 16³) + (8 × 16²) + (7 × 16¹) + (3 × 16⁰)
```

In ES6 and later, we can also represent integers in binary(base-2) or octal(base-8) using prefixes **0b and 0o(or 0B and 0O)** respectively.

Examples:-

```plaintext
0b110110110        // => (1 × 2⁸) + (1 × 2⁷) + (0 × 2⁶) + (1 × 2⁵) + (1 × 2⁴) + (0 × 2³) + (1 × 2²) + (1 × 2¹) + (0 × 2⁰)
0o57246            // => (5 × 8⁴) + (7 × 8³) + (2 × 8²) + (4 × 8¹) + (6 × 8⁰)
```

### Floating Point literals

Floating-point literals can have decimal point.  
A real value is represented as an integral part of the number, followed by a decimal point and fractional part of the number.

Floating-point literals can also be represented using exponential notation.  
A real number followed by letter **e(or E)** with optional +/- sign, followed by an integer exponent.

This notation represents a real number multiplied by 10 to the power of the exponent.

Examples:-

```plaintext
3.14
55482.2287
7.9985e33     // => 7.9985 × 10²³
1.221533E-11  // => 1.221533 × 10⁻¹¹
```

**Note:-**

> We can use separators in numeric literals to make them easier to read.

Long numbers are hard to read, so ES2021 added underscore separators. They are ignored by the engine and exist purely for our eyes:

```js
const budget = 1_000_000;
console.log(budget);  // 1000000
 
const bytes = 0xFF_FF_FF;
console.log(bytes);   // 16777215
```

One catch — they only work in numeric literals in our source code. A string does not get the same treatment:

```js
console.log(Number("1_000"));  // NaN
```

So they are for numbers we type, not numbers that arrive from an input or an API.

### Why `0.1 + 0.2` is not `0.3`

Run this and JavaScript will look broken:

```js
console.log(0.1 + 0.2);          // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3);  // false
```

It is not broken, and it is not a JavaScript problem — Python, Java and C do exactly the same thing. Every number in JavaScript is a 64-bit binary float, and `0.1` cannot be represented exactly in binary, in the same way `1/3` cannot be written exactly in decimal. We store the closest available value, and the tiny errors add up.

So do not compare floats with `===`. Compare the difference against a tolerance:

```js
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);  // true
```

`Number.EPSILON` is the smallest gap between 1 and the next representable number — a reasonable tolerance for small values.

**For money, do not use floats at all.** Store the smallest unit as an integer — paise instead of rupees, cents instead of dollars — and divide only when displaying:

```js
const priceInPaise = 19999;                    // ₹199.99
const totalInPaise = priceInPaise * 3;
console.log(totalInPaise / 100);               // 599.97
```

Every rounding error in a billing system I have seen started with someone storing `199.99` in a float.

### The safe integer range

Floats also limit how large an integer can go before it stops being exact:

```js
console.log(Number.MAX_SAFE_INTEGER);      // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1);  // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2);  // 9007199254740992  <-- same answer
```

No error, just a wrong number. Past that limit, integers need `BigInt` — which we covered in the types post.

### Arithmetic in JavaScript

JavaScript program works with numbers using arithmetic operators, that language provides.  
These includes \**+, -, , /, and %.*

**%(Modulo)** is used to get a **remainder after division**.

ES2016 adds \*\* for exponentiation.

**Examples:-**

```plaintext
20**4 // => 160000
```

Arithmetic in JavaScript does not raise an error in case of overflow, underflow, or division by zero.  
When the number(or a result of the operation) is larger than the largest representable number(overflow), the resulting value is a special infinite value, **Infinity**.

Similarly, if the number(or a result of the operation) is smaller than the smallest representable value, the resulting special value is negative infinity, **\-Infinity**.

The zero divide by zero does not have a well-defined value and the result of this operation is a special not-a-number value(**NaN**).

### Checking for `NaN`

`NaN` is the only value in JavaScript that is not equal to itself:

```js
console.log(NaN === NaN);  // false
```

Which means `===` is useless for detecting it. There are two working checks, and one of them is a trap:

```js
console.log(isNaN("hello"));         // true  <-- converts the string first, then checks
console.log(Number.isNaN("hello"));  // false <-- "hello" is a string, not NaN
 
console.log(Number.isNaN(NaN));      // true
console.log(Object.is(NaN, NaN));    // true
```

The global `isNaN()` converts its argument to a number *before* checking, so it really answers "is this not a number?" rather than "is this `NaN`?". `Number.isNaN()` (ES6) does what the name says. Use that one.

### `parseInt`, `Number()`, and the radix

These two look interchangeable and are not:

```js
console.log(parseInt("12px"));  // 12  — reads until it hits something invalid
console.log(Number("12px"));    // NaN — the whole string must be a valid number
```

`parseInt` is forgiving, which is useful for reading CSS values and dangerous for validating user input. If a field must contain a number, `Number()` is the stricter check.

Always pass the radix as the second argument:

```js
console.log(parseInt("0x1F"));      // 31  — guessed hexadecimal
console.log(parseInt("0x1F", 10));  // 0   — read as decimal, stops at the "x"
console.log(parseInt("08", 10));    // 8
```

`Number.parseInt` and `Number.parseFloat` (ES6) are the exact same functions, just moved onto `Number` so they are not floating around as globals. Either works.

### `toFixed` and its rounding surprise

`toFixed` is the usual way to cut a number to a fixed number of decimals. Two things about it catch people out.

First, it returns a **string**:

```js
console.log(typeof (5).toFixed(2));  // "string"
console.log((5).toFixed(2) + 1);     // "5.001"  — string concatenation, not addition
```

Second, its rounding does not always match what you were taught in school:

```js
console.log((2.5).toFixed(0));    // "3"
console.log((1.5).toFixed(0));    // "2"
console.log((1.005).toFixed(2));  // "1.00"   <-- not "1.01"
```

That last one is the floating-point problem again. `1.005` is actually stored as something very slightly *below* 1.005, so rounding it down is technically correct. Which is another reason not to do money arithmetic in floats.

### `Intl.NumberFormat` for anything a user sees

For currency, percentages and thousand separators, do not hand-roll it. `Intl.NumberFormat` handles the locale rules for you — including Indian grouping, which is 2-2-3, not 3-3-3:

```js
const inr = new Intl.NumberFormat("en-IN", {
  style: "currency",
  currency: "INR",
});
 
console.log(inr.format(1234567.891));  // ₹12,34,567.89
```

Notice the grouping: `12,34,567`, not `1,234,567`. Writing that by hand with a regex is exactly the kind of thing that works in testing and breaks for half your users.

```js
console.log(new Intl.NumberFormat("en-IN").format(1234567));                       // 12,34,567
console.log(new Intl.NumberFormat("en-US", { notation: "compact" }).format(1234567)); // 1.2M
```

### The `Math` object

`Math` is a plain object of number utilities. It is not a constructor — there is no `new Math()`.

The ones worth knowing:

```js
Math.round(4.5);    // 5   — nearest, .5 rounds up
Math.floor(4.9);    // 4   — always down
Math.ceil(4.1);     // 5   — always up
Math.trunc(-4.9);   // -4  — just drops the decimals
Math.floor(-4.9);   // -5  — note the difference from trunc for negatives
 
Math.abs(-7);       // 7
Math.max(3, 9, 1);  // 9
Math.min(3, 9, 1);  // 1
Math.sqrt(16);      // 4
Math.pow(2, 10);    // 1024   — or just 2 ** 10
Math.random();      // 0 to (but not including) 1
```

`Math.max` takes separate arguments, not an array, so spread it:

```js
const scores = [42, 91, 7];
console.log(Math.max(...scores));  // 91
```

A random integer in a range comes up often enough to memorise:

```js
function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
 
console.log(randomInt(1, 6));  // a dice roll
```

One warning: `Math.random()` is not cryptographically secure. For tokens, passwords or anything a user should not be able to predict, use `crypto.getRandomValues()`.

### Date and Time

There is one more number-adjacent topic: dates. Internally a `Date` is just a number — milliseconds since 1 January 1970 UTC:

```js
console.log(Date.now());  // e.g. 1789200000000
```

But the `Date` API around that number has been a source of complaints for as long as JavaScript has existed — months counted from zero, mutable objects, no real timezone support. It finally has a replacement: the **Temporal API**, which reached browsers in 2026.

Dates deserve their own post rather than a paragraph here, so we will cover both properly later in this series.

That’s all I wanted to cover about Numbers data type in JavaScript. In an upcoming article in this series, I will cover the **Text data type of JavaScript** in detail.

**Hope you like it, if yes \*\*like & share.\*\***

**Thanks for your time.**

**Happy coding….**

[**← Types, values, and variables in JavaScript**](https://blog.ganeshjaiwal.dev/types-values-and-variables-in-javascript) [**JavaScript Strings →**](https://blog.ganeshjaiwal.dev/javascript-strings)\*
