Skip to content
JavaScript js numbers 4 min read

Number Basics

JavaScript has a single primitive type for everyday arithmetic: Number. Whether you write 42, 3.14, or 0xff, the language stores it the same way — as a 64-bit IEEE-754 double-precision float. Understanding this one decision explains almost every numeric quirk you will ever hit, from why 0.1 + 0.2 is not quite 0.3 to why huge integers silently lose precision. This page covers how numbers are represented, the literal forms you can write, and the special values worth knowing.

One type for everything

Unlike languages such as Java or C that distinguish int, long, float, and double, JavaScript collapses all of them into one Number type. Every numeric value is a double, giving you about 15–17 significant decimal digits of precision. typeof confirms there is no separate integer type.

console.log(typeof 42);    // both are the same type
console.log(typeof 3.14);
console.log(typeof -0.5);

Output:

number
number
number

Because integers are just doubles with no fractional part, 5 / 2 gives 2.5 rather than truncating — there is no integer division operator. Use Math.trunc() or Math.floor() when you want a whole result.

The only other numeric primitive is BigInt, a distinct type for arbitrarily large integers written with an n suffix (10n). It does not mix with Number in arithmetic. See the BigInt page for details.

Literal forms

You can write the same value in several notations. All of them produce ordinary Number values — the syntax is just for your convenience as the author.

Literal formExampleDecimal value
Decimal255255
Hexadecimal (0x)0xff255
Binary (0b)0b1111_1111255
Octal (0o)0o377255
Exponential2.55e2255
Float0.5, .50.5
console.log(0xff);        // hexadecimal
console.log(0b1111_1111); // binary
console.log(0o377);       // octal
console.log(2.55e2);      // exponential: 2.55 x 10^2
console.log(.5);          // leading-zero optional

Output:

255
255
255
255
0.5

Exponential (scientific) notation uses e (or E) followed by a signed power of ten. It works for very large or very small magnitudes: 1.5e3 is 1500 and 2e-3 is 0.002.

Legacy octal literals with a leading zero (0377) are forbidden in strict mode and modules. Always use the explicit 0o prefix instead.

Numeric separators

Since ES2021 you can insert underscores (_) between digits to make large literals readable. They are purely visual and are stripped before evaluation. They work in every radix and in the fractional/exponent parts too.

const budget = 1_000_000;
const rgb = 0xff_00_ff;
const card = 1234_5678_9012_3456;
const tiny = 0.000_001;
console.log(budget, rgb, tiny);

Output:

1000000 16711935 0.000001

Separators cannot lead, trail, or sit next to the radix prefix — _100, 100_, and 0x_ff are all syntax errors.

Infinity and negative zero

Some results overflow the representable range or are mathematically undefined. JavaScript expresses these with special values rather than throwing.

  • Infinity and -Infinity represent values too large to store, and are also produced by division by zero.
  • -0 (negative zero) is distinct from 0 in the bit pattern, though they compare as equal.
console.log(1 / 0);            // Infinity
console.log(-1 / 0);           // -Infinity
console.log(1e308 * 10);       // overflow to Infinity

console.log(0 === -0);         // true (loose check)
console.log(Object.is(0, -0)); // false (distinguishes them)
console.log(1 / -0);           // -Infinity reveals the sign

Output:

Infinity
-Infinity
Infinity
true
false
-Infinity

Object.is() is the reliable way to tell 0 from -0, since both === and == treat them as equal. Negative zero rarely matters, but it can surface in graphics math or when dividing by a signed zero.

Safe integer range

A double can represent every integer exactly only up to 2^53. Beyond that, the gaps between representable values grow larger than 1, so consecutive integers start to collide. The constant Number.MAX_SAFE_INTEGER marks the boundary.

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991

console.log(9007199254740992 === 9007199254740993); // true — collision!
console.log(Number.isSafeInteger(2 ** 53));          // false

Output:

9007199254740991
-9007199254740991
true
false

When you must work with integers larger than this — database IDs, timestamps in nanoseconds, cryptographic values — reach for BigInt, which has no upper bound.

Best practices

  • Treat all numeric literals as one Number type; pick the radix (hex, binary, decimal) that best documents intent.
  • Use numeric separators (1_000_000) for any literal with more than a few digits to aid readability.
  • Prefer the explicit 0o octal prefix over the legacy leading-zero form, which is invalid in strict mode.
  • Check Number.isSafeInteger(n) before doing integer math on values that might exceed 2^53, and switch to BigInt when needed.
  • Use Object.is(x, -0) rather than === when negative zero must be distinguished.
  • Remember division never truncates — use Math.trunc() or Math.floor() for integer-style results.
Last updated June 1, 2026
Was this helpful?