JavaScript’s Number
covers integers and floating-point values with one implementation: IEEE-754 binary64 (aka “double precision”). That means you get roughly 15–17 decimal digits of precision, support for NaN
, Infinity
, and signed zero (+0
and -0
), and a whole family of helpers on Number.prototype
and Number.*
. If you’ve ever seen 0.1 + 0.2
produce 0.30000000000000004
, you’ve bumped into this representation. MDN’s overview and the spec both confirm that JavaScript has exactly two numeric types: Number
(binary64) and BigInt
.
A Number
is stored as 64 bits: 1 sign bit, 11 bits of exponent (biased), and 52 bits of fraction with an implicit leading 1, giving 53 bits of precision. That’s why only integers in [-(2^53 − 1), 2^53 − 1]
are safe: beyond that range, you can’t uniquely represent every integer.
Number.MAX_SAFE_INTEGER; // 9007199254740991 (2**53 - 1)
Number.MIN_SAFE_INTEGER; // -9007199254740991
Number.isSafeInteger(42); // true
Number.isSafeInteger(2**53); // false
Those “safe integer” constants and checks are part of the standard library so you can proactively guard domain logic.
Literals, bases, and readability
You can write numbers in decimal, binary (0b...
), octal (0o...
), or hex (0x...
). You can also use numeric separators (_
) anywhere digits appear to improve readability—just not at the start, end, next to a decimal point, or doubled.
const oneTrillion = 1_000_000_000_000;
const hexMask = 0xFF_FF_00_00;
const permissions = 0b1111_0000_1010_0101;
const precise = 1_234.567_89;
Special numeric values you should care about
NaN
represents an invalid numeric result. Infinity
and -Infinity
represent overflow and division by zero. Number.MIN_VALUE
is the smallest positive subnormal binary64 value (≈ 5e−324), not the most negative number; it’s easy to misinterpret that name.
0 / 0; // NaN
1 / 0; // Infinity
-1 / 0; // -Infinity
Number.MIN_VALUE; // 5e-324 (smallest positive, not "most negative")
Equality, sameness, and the weirdness of -0
===
works for most comparisons, but it treats +0
and -0
as equal and it considers NaN !== NaN
. Object.is
follows the spec’s SameValue
algorithm: it distinguishes -0
from +0
and treats NaN
as the same value as itself. Reach for Object.is
when those edge cases matter.
NaN === NaN; // false
Object.is(NaN, NaN); // true
0 === -0; // true
Object.is(0, -0); // false
Parsing, coercion, and “is this a number?”
Prefer the Number.*
family over the legacy globals. Number.isNaN
only returns true
for the actual number NaN
and doesn’t coerce. Number.parseInt
and Number.parseFloat
parse strings like their global counterparts but live under Number
for modularity.
Number.isNaN('NaN'); // false (no coercion)
isNaN('NaN'); // true (legacy coercion)
Number.parseInt('42px', 10); // 42
Number.parseFloat('3.14ms'); // 3.14
Number(' 12 '); // 12 (coerces whole string or fails to NaN)
If you need to validate integers specifically, use Number.isInteger
. For the safe 53-bit range, pair it with Number.isSafeInteger
.
Number.isInteger(3.0); // true
Number.isInteger(3.14); // false
Formatting and rounding without surprises
Three standard formatters exist on the instance:
const n = 1234.56789;
n.toFixed(2); // "1234.57" -> fixed decimal places
n.toPrecision(4); // "1235" -> total significant digits (may use exponent)
n.toExponential(3); // "1.235e+3" -> scientific notation
Use toFixed
when you need a consistent number of decimals (like 2 for money display), and toPrecision
when you care about significant digits. Both return strings and round as needed.
Dealing with floating-point error like a pro
Binary floating point cannot represent many base-10 fractions exactly; 0.1
and 0.2
are approximations, so their sum is a slightly larger approximation than 0.3
. The canonical demo:
0.1 + 0.2 === 0.3; // false
0.1 + 0.2; // 0.30000000000000004
This is inherent to IEEE-754, not a JavaScript bug. You can compare within a tolerance using Number.EPSILON
(the gap between 1 and the next representable number).
function nearlyEqual(a, b, epsilon = Number.EPSILON) {
return Math.abs(a - b) <= epsilon * Math.max(1, Math.abs(a), Math.abs(b));
}
nearlyEqual(0.1 + 0.2, 0.3); // true
When the domain allows it, store scaled integers (cents instead of dollars) and only format at the edges. For truly large integers or exact integer math beyond 53 bits, use BigInt
.
Converting to and from Number safely
Everything that looks like a number is not necessarily safe to coerce. Number(value)
is an all-or-nothing conversion: the entire string must be numeric (after trimming) or you get NaN
. parseInt
/parseFloat
will read a leading numeric portion and stop at the first invalid character. Choose based on whether partial parses are desirable.
Number('12px'); // NaN
parseInt('12px', 10); // 12
Number('0xFF'); // 255 (hex string is allowed)
parseInt('0xFF'); // 255 (base inferred as 16 unless radix passed)
Boxing: Number vs number
Number
(with capital N) is the constructor for wrapper objects around primitive numbers. You almost never need to create a Number
object—primitives are faster and behave better. Methods on Number.prototype
are available to primitives via automatic boxing.
typeof 5; // "number"
typeof new Number(5); // "object"
(5).toFixed(1); // "5.0" (primitive calls method just fine)
MDN’s Number
reference documents both the constructor and the static/instance APIs; prefer primitives unless you’re interfacing with an API that explicitly expects an object.
Commonly used static properties at a glance
Static properties supply important boundaries and sentinels:
Number.MAX_VALUE; // ≈ 1.7976931348623157e+308 (largest finite)
Number.MIN_VALUE; // ≈ 5e-324 (smallest positive subnormal)
Number.POSITIVE_INFINITY; // Infinity
Number.NEGATIVE_INFINITY; // -Infinity
Number.NaN; // NaN (same value as global NaN)
Infinity
is also a global. Its behavior matches IEEE-754 rules and can show up from operations like division by zero.
Numbers and BigInts: where the line is
Numbers are ideal for real-valued calculations, measurements, and anything that reasonably fits within 53 bits of integer precision. BigInt
gives you arbitrary-precision integers but can’t mix directly with Number
, nor is it supported by Math.*
. Convert deliberately, only when you must, and keep arithmetic homogeneous.
const big = 2n ** 60n; // BigInt
// big + 1 // TypeError: can't mix BigInt and other types
Number(big) + 1; // OK, but may lose precision for larger values
Practical recipes
Clamp, round, and format for display
function clamp(x, min, max) {
return Math.min(max, Math.max(min, x));
}
function toMoney(amount) {
// Display only: do not use for internal accounting
return Number(amount).toFixed(2);
}
clamp(1.234, 0, 1); // 1
toMoney(12.5); // "12.50"
Parse robustly from user input
function readInt(input, { radix = 10 } = {}) {
const n = Number.parseInt(String(input).trim(), radix);
return Number.isNaN(n) ? null : n;
}
readInt(' 42px'); // 42
readInt('0xff'); // 255
readInt('hello'); // null
Compare with tolerance
// Useful when the exact bit pattern isn’t stable, but magnitude is.
function approximately(a, b, relTol = 1e-12, absTol = Number.EPSILON) {
const diff = Math.abs(a - b);
return diff <= Math.max(relTol * Math.max(Math.abs(a), Math.abs(b)), absTol);
}
approximately(Math.sqrt(2)**2, 2); // true
Performance and correctness tips you’ll actually use
Stick with primitives. Treat Number
as a value type and avoid constructing wrapper objects. Prefer Object.is
when you must tell -0
from 0
or want NaN
equality. Use Number.isNaN
and Number.isInteger
to avoid implicit coercions that can hide bugs. Reach for Number.EPSILON
-based comparisons when dealing with computed results, and reach for BigInt
when integer precision beyond 53 bits is a hard requirement. These choices align with the spec’s equality semantics and MDN’s API contracts.
References
Source | What it covers |
---|---|
MDN: Number (global object) | The Number type, safe integer range, API surface, and special values. |
ECMA-262 (HTML, 2024) | Normative definition of numeric types in ECMAScript, including Number and BigInt . |
ECMA-262 (HTML, “Data Types and Values”) | Equality algorithms (Number::equal , SameValue , SameValueZero ) that explain Object.is , NaN , and signed zero behavior. |
MDN: Number.EPSILON | Meaning of EPSILON and its use for tolerance comparisons. |
Wikipedia: Double-precision floating-point format | Binary64 layout (sign, exponent, fraction) and precision implications. |
MDN: Lexical grammar (numeric separators) | Rules and limitations for _ in numeric literals. |
MDN: Numbers and strings (guide) | Literal syntaxes, exponents, and separators in practice. |
MDN: Number.isNaN() | Robust NaN checking without coercion. |
MDN: Number.isInteger() | Integer detection semantics for Number . |
MDN: Number.isSafeInteger() | Checking for integers within the 53-bit safe range. |
MDN: Number.toFixed() | Fixed-decimal formatting behavior and rounding. |
MDN: Object.is() | Differences from === and treatment of NaN and signed zero. |
MDN: Infinity / Number.POSITIVE_INFINITY / Number.NEGATIVE_INFINITY | Infinity semantics and relationships to arithmetic and overflow. |
MDN: Number.MIN_VALUE | Clarification that it’s the smallest positive value, not “most negative”. |
MDN: Number.MAX_SAFE_INTEGER | The upper bound for exact integer representation in Number . |