jsguides

Number.prototype.toString()

toString(radix)

The toString() method returns a string representing the number. By default it uses base 10, but you can pass any radix from 2 to 36 to represent the number in binary, octal, hexadecimal, or any other base. Letters a through z represent digit values 10 through 35 in bases above 10.

This is the standard method for number-to-string conversion. Unlike string concatenation ('' + n), toString() lets you control the output base, making it practical for tasks like generating hex color codes or encoding compact identifiers.

Syntax

num.toString(radix)

Parameters

ParameterTypeDescription
radixnumberThe base of the number system (2–36). Defaults to 10.

Return value

A string representation of the number in the specified radix. For negative numbers the sign is preserved (e.g., (-255).toString(16) gives "-ff").

Examples

Decimal (default)

const num = 255;

num.toString();      // "255"
(42).toString();     // "42"
(0).toString();      // "0"

When calling toString() on a numeric literal you must wrap the literal in parentheses — the parser would otherwise treat the dot as a decimal point. Once you understand the default decimal behavior, changing the radix opens up conversions to other common bases like binary.

Binary (base 2)

const num = 255;

num.toString(2);           // "11111111"
(128).toString(2);         // "10000000"
(15).toString(2);          // "1111"

Binary output gives you the raw bit pattern — each digit is either 0 or 1. Octal (base 8) groups bits into chunks of three, producing more compact representations. It’s still used in some contexts like Unix file permissions, though hexadecimal has largely replaced it in modern code.

Octal (base 8)

const num = 255;

num.toString(8);           // "377"
(64).toString(8);          // "100"
(8).toString(8);           // "10"

Octal’s digit set is 0–7, so 8.toString(8) is "10" — just like decimal wraps at 10 and hex wraps at 16. Hexadecimal (base 16) uses digits 0–9 plus letters a–f, packing 4 bits per character. It’s the go-to for colors, memory addresses, and binary data dumps.

Hexadecimal (base 16)

const num = 255;

num.toString(16);          // "ff"
(255).toString(16);        // "ff"
(16).toString(16);         // "10"
(0).toString(16);          // "0"

Hex output is lowercase by default. Call .toUpperCase() on the result if you need "FF" style output. The radix parameter accepts any integer from 2 to 36, not just the familiar powers of two. Above base 16, the method uses letters as digits — a is 10, b is 11, up through z for 35.

Using letters for bases above 16

(35).toString(36);          // "z" (z = 35 in base 36)
(36).toString(36);         // "10" (36 = 1*36 + 0)
(100).toString(36);        // "2s" (2*36 + 28 = 100, s = 28)

Base 36 is useful for generating short alphanumeric identifiers from numeric IDs, since it uses all digits and lowercase letters. A practical use of toString(16) is converting RGB color channels to hex strings for CSS. The next example shows how to build a complete #rrggbb color code from three decimal channel values.

Practical example: color conversion

function rgbToHex(r, g, b) {
  const toHex = (n) => {
    const hex = n.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
  };
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

rgbToHex(255, 128, 0);   // "#ff8000"
rgbToHex(0, 0, 0);       // "#000000"
rgbToHex(255, 255, 255); // "#ffffff"

Hex conversion for colors treats each channel as an independent 2-digit value. Another common use of toString(2) is inspecting bitmasks — converting a set of permission flags to binary lets you see at a glance which bits are set, without doing mental base-2 arithmetic.

Practical example: binary flags

const FLAG_READ = 1;    // 0001
const FLAG_WRITE = 2;   // 0010
const FLAG_EXEC = 4;    // 0100

const permissions = FLAG_READ | FLAG_WRITE;  // 3 (0011)

console.log(permissions.toString(2));  // "11"

Converting permission bitmasks to binary strings makes it easy to inspect and log which flags are set. The method also handles a few edge cases cleanly: negative numbers preserve their sign in the output, zero always produces "0" regardless of radix, and floating-point values convert directly without requiring an integer argument.

Edge cases

(-255).toString(16);    // "-ff"
(0).toString(2);       // "0"
(1.5).toString();      // "1.5"

These edge cases show that toString() handles negatives, zeros, and floats consistently. But one practical detail catches newcomers: the method does not zero-pad its output, so converting 5 to hex gives "5", not "05". This matters whenever you need fixed-width output.

Padding hex output

toString(16) does not zero-pad its output. A value of 5 produces "5", not "05". When generating two-digit hex strings (as in the RGB-to-hex example), you must add the padding manually:

const padHex = (n) => n.toString(16).padStart(2, '0');

console.log(padHex(5));    // "05"
console.log(padHex(255));  // "ff"
console.log(padHex(16));   // "10"

String.prototype.padStart() is the standard method for this. The second argument '0' is the character to use for padding, and 2 is the minimum length.

Rounding and floating-point output

toString() on a floating-point number produces the shortest string that uniquely identifies the value. For numbers with many decimal places, this may be shorter than you expect:

(1.1).toString();   // "1.1"
(0.1 + 0.2).toString(); // "0.30000000000000004"

For formatted output with a fixed number of decimal places, use toFixed() or toPrecision() instead.

See Also