JavaScript Number Methods

6 minute read

JavaScript has two distinct sets of numeric helpers: instance methods on numbers themselves ((3.14).toFixed(2)) and static utilities on the Number global (Number.isInteger(x)). Plus a handful of legacy globals (parseInt, parseFloat, isNaN) that you’ll see in old code. This chapter is the reference for all three families.

Instance Methods — Called on a Number

Method Returns Example
toString(radix?) String form (255).toString(16) → "ff"
toFixed(digits) String with fixed decimals (3.14159).toFixed(2) → "3.14"
toPrecision(digits) String with N significant digits (1234.5).toPrecision(3) → "1.23e+3"
toExponential(digits?) Scientific notation string (1234).toExponential(2) → "1.23e+3"
toLocaleString(loc?, opts?) Locale-aware display (1234).toLocaleString("en") → "1,234"
valueOf() Primitive value Rarely used directly

toString(radix)

Convert a number to a string, optionally in a different base (2–36):

(42).toString()        // "42"
(255).toString(16)     // "ff"
(255).toString(2)      // "11111111"
(10).toString(8)       // "12"
(3.14).toString()      // "3.14"
// Wrap negatives in parentheses or assign to a variable:
const x = -42;
x.toString();          // "-42"
(-42).toString();      // "-42"
-42.toString();        // ❌ SyntaxError — parsed as -(42.toString())

toFixed(digits)

Round to a fixed number of decimal places. Returns a string.

(3.14159).toFixed(2)   // "3.14"
(3.14159).toFixed(0)   // "3"
(1).toFixed(2)         // "1.00"
(1.005).toFixed(2)     // "1.00"   ← surprising! float precision
(1.5).toFixed(0)       // "2"      ← banker's rounding edge cases vary

// Convert back if you need a number
Number((3.14159).toFixed(2));   // 3.14
+(3.14159).toFixed(2);          // 3.14

Warning: toFixed returns a string, not a number. Don’t use it for math; convert back with Number() or unary +.

toPrecision(digits)

Total significant digits, not decimal places.

(123.456).toPrecision(2)   // "1.2e+2"
(123.456).toPrecision(5)   // "123.46"
(0.001234).toPrecision(2)  // "0.0012"

toExponential(digits?)

Scientific notation.

(1234).toExponential()      // "1.234e+3"
(1234).toExponential(2)     // "1.23e+3"
(0.001234).toExponential()  // "1.234e-3"

toLocaleString(locale?, options?)

The everyday formatter for humans. Locale-aware, supports currency, percent, and units.

(1234567.89).toLocaleString()                     // depends on system locale
(1234567.89).toLocaleString("en-US")              // "1,234,567.89"
(1234567.89).toLocaleString("de-DE")              // "1.234.567,89"
(1234567.89).toLocaleString("hi-IN")              // "12,34,567.89"

(0.4321).toLocaleString("en", { style: "percent" });
// "43%"

(1999.99).toLocaleString("en", {
  style: "currency",
  currency: "USD"
});
// "$1,999.99"

(1024).toLocaleString("en", {
  style: "unit",
  unit: "megabyte"
});
// "1,024 MB"

For repeated formatting, always cache an Intl.NumberFormat:

// ❌ Slow — creates a new formatter for every value
items.map(x => x.toLocaleString("en", { style: "currency", currency: "USD" }));

// ✅ Fast — reuse one formatter
const fmt = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD"
});
items.map(x => fmt.format(x));

Static Methods on Number

These live on Number itself, not on instances. They’re the modern, strict versions.

Static What It Does
Number.isInteger(x) True only for integers
Number.isFinite(x) True for real numbers (not Infinity or NaN)
Number.isNaN(x) True only for NaN — no coercion
Number.isSafeInteger(x) True if integer within ±(2⁵³−1)
Number.parseInt(s, radix) Same as global parseInt
Number.parseFloat(s) Same as global parseFloat
Number.EPSILON Smallest 1 + ε ≠ 1
Number.MAX_SAFE_INTEGER 2⁵³ − 1
Number.MAX_VALUE Largest representable float
Number.MIN_VALUE Smallest positive float
Number.POSITIVE_INFINITY Infinity
Number.NEGATIVE_INFINITY -Infinity
Number.NaN NaN
Number.isInteger(42)         // true
Number.isInteger(42.0)       // true   (no decimal part)
Number.isInteger(42.5)       // false
Number.isInteger("42")       // false  ← strict — string is not a number

Number.isFinite(1)           // true
Number.isFinite(Infinity)    // false
Number.isFinite(NaN)         // false
Number.isFinite("42")        // false  ← strict, no coercion

Number.isNaN(NaN)            // true
Number.isNaN("hi")           // false  ← strict
Number.isNaN(undefined)      // false

Number.isSafeInteger(2 ** 53 - 1)   // true
Number.isSafeInteger(2 ** 53)       // false

Global isNaN, isFinite, parseInt, parseFloat (Legacy)

Older, more coercive versions:

// Globals coerce to number first — usually misleading
isNaN("hello")        // true   — "hello" coerces to NaN
isNaN(undefined)      // true
isNaN("42")           // false  — "42" coerces to 42
isFinite("42")        // true   — coerces

Number.isNaN("hello") // false  ← strict, no coercion
Number.isNaN(NaN)     // true
Use Don’t Use
Number.isNaN(x) isNaN(x)
Number.isFinite(x) isFinite(x)
Number.parseInt(x, 10) parseInt(x, 10) (still works but global)

parseInt quirks worth remembering:

parseInt("42")              // 42
parseInt("42px")            // 42      ← strips trailing junk
parseInt("0x10")            // 16      ← recognises 0x prefix
parseInt("10", 2)           // 2       ← binary
parseInt("10", 16)          // 16      ← hex
parseInt("", 10)            // NaN
parseInt("abc", 10)         // NaN

// ❌ Without radix, old engines treated "08" as octal
parseInt("08")              // 8 in modern engines, but ALWAYS pass radix
parseInt("08", 10)          // 8
parseFloat("3.14")          // 3.14
parseFloat("3.14abc")       // 3.14
parseFloat("abc")           // NaN
parseFloat("1e3")           // 1000

Tip: Always pass parseInt(s, 10). It’s a one-character habit that prevents the most embarrassing JS bug.

The Math Object — Most-Used Helpers

Full chapter is 23. The everyday subset:

Math.abs(-7)       // 7
Math.sign(-5)      // -1
Math.sign(0)       // 0
Math.sign(5)       // 1

Math.round(2.5)    // 3
Math.round(-2.5)   // -2     ← rounds toward +∞
Math.floor(2.9)    // 2
Math.ceil(2.1)     // 3
Math.trunc(-2.9)   // -2     ← chops decimal

Math.min(3, 1, 2)  // 1
Math.max(3, 1, 2)  // 3
Math.min(...arr)   // spread to apply to array

Math.sqrt(81)      // 9
Math.cbrt(27)      // 3
Math.pow(2, 10)    // 1024   (use 2 ** 10 instead)
Math.hypot(3, 4)   // 5      sqrt(3² + 4²)

Math.log(Math.E)   // 1
Math.log2(1024)    // 10
Math.log10(1000)   // 3

Math.random()      // 0 ≤ x < 1

Random Integer Pattern

function randInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

randInt(1, 6);   // 1..6
randInt(0, 99);  // 0..99

Converting To/From Numbers — Cheatsheet

// String → Number
Number("42")           // 42
+("42")                // 42
parseInt("42px", 10)   // 42
parseFloat("3.14")     // 3.14

// Number → String
(42).toString()        // "42"
String(42)             // "42"
`${42}`                // "42"
42 + ""                // "42"  (works but ugly)
(255).toString(16)     // "ff"

// Number → Boolean
Boolean(0)             // false
Boolean(42)            // true
!!42                   // true

// Boolean → Number
Number(true)           // 1
+true                  // 1

A Realistic Example — Safe Number Parser

/**
 * Strict parser: returns a number, or null for any non-numeric input.
 */
function parseNumeric(input, { min, max, integer = false } = {}) {
  const n = Number(input);
  if (!Number.isFinite(n))     return null;
  if (integer && !Number.isInteger(n)) return null;
  if (min !== undefined && n < min) return null;
  if (max !== undefined && n > max) return null;
  return n;
}

parseNumeric("42");                          // 42
parseNumeric("3.14");                        // 3.14
parseNumeric("3.14", { integer: true });     // null
parseNumeric("42",  { min: 0, max: 100 });   // 42
parseNumeric("200", { min: 0, max: 100 });   // null
parseNumeric("abc");                         // null
parseNumeric("");                            // null   ← Number("") is 0, but isFinite(0) is true... let's recheck

Wait — Number("") is actually 0, and 0 is finite. If you want empty string to fail:

function parseNumeric(input, opts = {}) {
  if (typeof input === "string" && input.trim() === "") return null;
  // ... rest as above
}

This is exactly why writing a strict parser takes more lines than you’d expect — JavaScript’s coercion has corners everywhere.

Common Mistakes

// ❌ toFixed returns a string, used as a number
const total = (price * 3).toFixed(2);
sum += total;     // "9.00" + "9.00" → "9.009.00"

// ✅
sum += Number((price * 3).toFixed(2));

// ❌ Global isNaN
isNaN("hello");     // true — confusing!

// ✅
Number.isNaN(value); // strict

// ❌ parseInt without radix
parseInt("08");      // Usually 8, but historically risky

// ✅
parseInt("08", 10);

// ❌ Math.round for halves
Math.round(-0.5);    // 0     ← rounds toward +∞ (away from -∞)
Math.round(0.5);     // 1

// Use Math.trunc when you specifically want to chop

Best Practices Checklist

  • ✅ Prefer Number.isNaN, Number.isFinite, Number.isInteger over globals
  • ✅ Always pass radix to parseInt
  • ✅ Cache Intl.NumberFormat instances when formatting many values
  • ✅ Convert toFixed output back to number if you’ll do math on it
  • ✅ Use Number.isSafeInteger before trusting large integer IDs
  • ❌ Don’t use legacy globals (isNaN, isFinite) — they coerce silently
  • ❌ Don’t compare floats with === after toFixed (compare numbers with epsilon instead)
  • ❌ Don’t use Math.round if you specifically want banker’s rounding — it doesn’t

What’s Next?

Strings and numbers covered. Now the workhorse collection type: arrays. We’ll create them, mutate them, destructure them, and meet the iteration methods that make modern JavaScript so expressive.

← Previous: JS Numbers Next: JS Arrays →

Updated: