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 annsuffix (10n). It does not mix withNumberin 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 form | Example | Decimal value |
|---|---|---|
| Decimal | 255 | 255 |
Hexadecimal (0x) | 0xff | 255 |
Binary (0b) | 0b1111_1111 | 255 |
Octal (0o) | 0o377 | 255 |
| Exponential | 2.55e2 | 255 |
| Float | 0.5, .5 | 0.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 explicit0oprefix 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.
Infinityand-Infinityrepresent values too large to store, and are also produced by division by zero.-0(negative zero) is distinct from0in 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
Numbertype; 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
0ooctal 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 toBigIntwhen needed. - Use
Object.is(x, -0)rather than===when negative zero must be distinguished. - Remember division never truncates — use
Math.trunc()orMath.floor()for integer-style results.