Number to Words: How to Convert Any Number to English Text
Writing a cheque, generating an invoice, or building a voice assistant — all of them need to spell out numbers in plain English. "1,042" becomes "one thousand forty-two". The conversion looks trivial until you hit the teens (11–19 break the pattern), the hundreds, the millions, and then zero. This guide explains the algorithm from scratch and shows working code in four languages.
Why Number-to-Words Is Trickier Than It Looks
English number names are almost regular — almost:
| Range | Pattern breaks down? |
|---|---|
| 1–9 | Regular (one, two, …, nine) |
| 10 | Special ("ten", not "onety") |
| 11–19 | All irregular (eleven, twelve, thirteen … nineteen) |
| 20–99 | Regular again (twenty-one, thirty-five…) |
| 100–999 | Regular with "hundred" |
| 1,000+ | Group of three digits repeating with scale words (thousand, million, billion…) |
The irregularity of 10–19 is the only real exception to handle.
The Algorithm
Every number can be broken into groups of three digits from right to left:
1,042,007,305
= 1 · billion + 042 · million + 007 · thousand + 305
Each group of three is converted independently, then a scale word (thousand, million, billion, trillion…) is appended.
Step 1 — Words for 1–19
ones = ["", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve",
"thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
"eighteen", "nineteen"]
Step 2 — Tens words (20, 30, … 90)
tens = ["", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety"]
Step 3 — Convert a three-digit group
function threeDigits(n):
if n == 0: return ""
if n < 20: return ones[n]
if n < 100: return tens[n/10] + (ones[n%10] ? "-" + ones[n%10] : "")
return ones[n/100] + " hundred" + (n%100 ? " " + threeDigits(n%100) : "")
Step 4 — Assemble with scale words
scales = ["", "thousand", "million", "billion", "trillion"]
function numberToWords(n):
if n == 0: return "zero"
if n < 0: return "negative " + numberToWords(-n)
parts = []
i = 0
while n > 0:
group = n % 1000
if group != 0:
chunk = threeDigits(group)
if scales[i]: chunk += " " + scales[i]
parts.prepend(chunk)
n = n / 1000
i++
return parts.join(" ")
Example trace for 1,042:
group 0 (42): "forty-two" + "" → "forty-two"
group 1 (1): "one" + "thousand" → "one thousand"
result: "one thousand forty-two"
Code Examples
JavaScript
const ones = [
'', 'one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve',
'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen',
'eighteen', 'nineteen',
];
const tens = [
'', '', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety',
];
const scales = ['', 'thousand', 'million', 'billion', 'trillion'];
function threeDigits(n) {
if (n === 0) return '';
if (n < 20) return ones[n];
if (n < 100) {
const t = tens[Math.floor(n / 10)];
const o = ones[n % 10];
return o ? `${t}-${o}` : t;
}
const h = ones[Math.floor(n / 100)];
const rem = n % 100;
return rem ? `${h} hundred ${threeDigits(rem)}` : `${h} hundred`;
}
function numberToWords(n) {
if (!Number.isInteger(n)) throw new Error('Integer required');
if (n === 0) return 'zero';
if (n < 0) return 'negative ' + numberToWords(-n);
const parts = [];
let i = 0;
while (n > 0) {
const group = n % 1000;
if (group !== 0) {
const chunk = threeDigits(group);
parts.unshift(scales[i] ? `${chunk} ${scales[i]}` : chunk);
}
n = Math.floor(n / 1000);
i++;
}
return parts.join(' ');
}
console.log(numberToWords(0)); // zero
console.log(numberToWords(42)); // forty-two
console.log(numberToWords(1042)); // one thousand forty-two
console.log(numberToWords(1000000)); // one million
console.log(numberToWords(-7)); // negative seven
console.log(numberToWords(1000000000)); // one billion
Python
ones = [
'', 'one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve',
'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen',
'eighteen', 'nineteen',
]
tens = [
'', '', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety',
]
scales = ['', 'thousand', 'million', 'billion', 'trillion']
def three_digits(n: int) -> str:
if n == 0:
return ''
if n < 20:
return ones[n]
if n < 100:
t, o = tens[n // 10], ones[n % 10]
return f'{t}-{o}' if o else t
h, rem = ones[n // 100], n % 100
return f'{h} hundred {three_digits(rem)}' if rem else f'{h} hundred'
def number_to_words(n: int) -> str:
if not isinstance(n, int):
raise TypeError('Integer required')
if n == 0:
return 'zero'
if n < 0:
return 'negative ' + number_to_words(-n)
parts: list[str] = []
i = 0
while n > 0:
group = n % 1000
if group:
chunk = three_digits(group)
parts.insert(0, f'{chunk} {scales[i]}' if scales[i] else chunk)
n //= 1000
i += 1
return ' '.join(parts)
print(number_to_words(0)) # zero
print(number_to_words(42)) # forty-two
print(number_to_words(1_042)) # one thousand forty-two
print(number_to_words(1_000_000)) # one million
print(number_to_words(-7)) # negative seven
Go
package main
import (
"fmt"
"strings"
)
var ones = []string{
"", "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", "eleven", "twelve",
"thirteen", "fourteen", "fifteen", "sixteen", "seventeen",
"eighteen", "nineteen",
}
var tens = []string{
"", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety",
}
var scales = []string{"", "thousand", "million", "billion", "trillion"}
func threeDigits(n int) string {
switch {
case n == 0:
return ""
case n < 20:
return ones[n]
case n < 100:
t, o := tens[n/10], ones[n%10]
if o != "" {
return t + "-" + o
}
return t
default:
h, rem := ones[n/100], n%100
if rem != 0 {
return h + " hundred " + threeDigits(rem)
}
return h + " hundred"
}
}
func numberToWords(n int) string {
if n == 0 {
return "zero"
}
if n < 0 {
return "negative " + numberToWords(-n)
}
var parts []string
for i := 0; n > 0; i++ {
if group := n % 1000; group != 0 {
chunk := threeDigits(group)
if scales[i] != "" {
chunk += " " + scales[i]
}
parts = append([]string{chunk}, parts...)
}
n /= 1000
}
return strings.Join(parts, " ")
}
func main() {
fmt.Println(numberToWords(0)) // zero
fmt.Println(numberToWords(42)) // forty-two
fmt.Println(numberToWords(1042)) // one thousand forty-two
fmt.Println(numberToWords(1_000_000)) // one million
fmt.Println(numberToWords(-7)) // negative seven
}
PHP
<?php
$ones = ['', 'one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve',
'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen',
'eighteen', 'nineteen'];
$tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety'];
$scales = ['', 'thousand', 'million', 'billion', 'trillion'];
function threeDigits(int $n, array $ones, array $tens): string {
if ($n === 0) return '';
if ($n < 20) return $ones[$n];
if ($n < 100) {
$t = $tens[(int)($n / 10)];
$o = $ones[$n % 10];
return $o ? "$t-$o" : $t;
}
$h = $ones[(int)($n / 100)];
$rem = $n % 100;
return $rem
? "$h hundred " . threeDigits($rem, $ones, $tens)
: "$h hundred";
}
function numberToWords(int $n, array $ones, array $tens, array $scales): string {
if ($n === 0) return 'zero';
if ($n < 0) return 'negative ' . numberToWords(-$n, $ones, $tens, $scales);
$parts = [];
$i = 0;
while ($n > 0) {
$group = $n % 1000;
if ($group !== 0) {
$chunk = threeDigits($group, $ones, $tens);
if ($scales[$i]) $chunk .= ' ' . $scales[$i];
array_unshift($parts, $chunk);
}
$n = (int)($n / 1000);
$i++;
}
return implode(' ', $parts);
}
echo numberToWords(0, $ones, $tens, $scales) . "\n"; // zero
echo numberToWords(42, $ones, $tens, $scales) . "\n"; // forty-two
echo numberToWords(1042, $ones, $tens, $scales) . "\n"; // one thousand forty-two
echo numberToWords(1000000, $ones, $tens, $scales) . "\n"; // one million
echo numberToWords(-7, $ones, $tens, $scales) . "\n"; // negative seven
Edge Cases to Handle
| Input | Expected output | Common mistake |
|---|---|---|
0 |
"zero" | Returning empty string |
-1 |
"negative one" | Passing negative to abs without prefixing |
100 |
"one hundred" | "one hundred zero" |
1000 |
"one thousand" | "one thousand zero" |
11 |
"eleven" | "ten-one" (treating teen as ten + ones) |
21 |
"twenty-one" | "twenty one" (missing hyphen per AP/Chicago style) |
1000000 |
"one million" | "one thousand thousand" |
Decimal numbers
The standard approach: convert integer and fractional parts separately.
42.50 → "forty-two and 50/100" (cheque/legal style)
42.50 → "forty-two point five" (spoken style)
For monetary amounts, cheque style ("and XX/100") is conventional in banking.
Scale Words Reference
| Power | Name |
|---|---|
| 10³ | thousand |
| 10⁶ | million |
| 10⁹ | billion |
| 10¹² | trillion |
| 10¹⁵ | quadrillion |
| 10¹⁸ | quintillion |
| 10²¹ | sextillion |
| 10²⁴ | septillion |
For most practical purposes (invoices, cheques, voice UI) you only need up to trillion.
Hyphen Rules (AP Style)
- Compound numbers twenty-one through ninety-nine always use a hyphen.
- "One hundred" has no hyphen — "one hundred forty-two" not "one-hundred-forty-two".
- Thousands and above: no hyphen between scale and group — "one thousand forty-two".
FAQ
Q: Why does "eleven" not follow the pattern?
English teens come from Old English and Proto-Germanic roots ("ain-lif" = "one left over ten"). They were established long before a regular system existed.
Q: Should I write "forty" or "fourty"?
Always "forty". "Fourty" is a common misspelling — there is no 'u' in forty.
Q: How do I handle ordinals (first, second, twenty-first)?
Convert to cardinal first, then apply the suffix: "one" → "first", "two" → "second", "three" → "third", everything else + "th" (with some adjustments: "five" → "fifth", "nine" → "ninth", "twelve" → "twelfth", "twenty" → "twentieth").
Q: What about "and" — "one hundred and two" vs "one hundred two"?
Both are valid. British English uses "and" after hundreds ("one hundred and two"). American English typically omits it ("one hundred two"). For international software, omitting "and" is safer.
Q: How large can I go?
With 64-bit integers (up to ~9.2 × 10¹⁸) you need up to "quintillion". JavaScript's Number type loses integer precision above 2⁵³ — use BigInt for larger values.
Try It Online
Use the Number to Words converter to instantly spell out any integer — including negative numbers and values up to trillions.