Converting a number to words is a formatting operation, not a security control. Written amounts can make a document easier to review and can expose a disagreement between two representations, but they do not replace authorization, signatures, tamper-evident storage, or the rules of a bank, court, tax authority, or contract. The exact wording also depends on language, locale, currency, document type, and the source value's precision.

TL;DR

  • Define the output contract before writing code: language, locale, cardinal or ordinal form, grouping style, currency, minor unit, and rounding policy.
  • Keep monetary values as integer minor units or exact decimal values. Do not derive cents from a binary floating-point subtraction.
  • British and American English differ in punctuation and the use of and; neither style is a universal check-writing rule.
  • Chinese ordinary numerals and Chinese financial numerals (大写金额) are different representations. Institutional forms must be checked against the applicable local rule.
  • Reject malformed, ambiguous, out-of-range, and rounded input instead of silently producing plausible text.

What Number-to-Words Conversion Does

The converter maps a validated numeric value to a language-specific string. It should not silently change the value, infer a currency, or decide whether a document is legally valid. A production interface should record at least:

Contract field Example
Input representation 123456 integer minor units
Language and locale en-US, en-GB, zh-CN
Number kind cardinal, ordinal, or currency
Currency and minor unit USD, 2 minor digits
Rounding reject, half-even, or half-up
Output policy hyphens, and, capitalization, check suffix

This separation matters when a value comes from JSON, a database, a spreadsheet, or an external API. JSON has no native arbitrary-precision integer type, and JavaScript Number cannot represent every integer above 2^53 - 1 exactly. Validate and preserve the source representation before conversion.

English Number Rules

Cardinals and Scale Words

Numbers from 21 to 99 normally use a hyphen when written as a standalone phrase: twenty-one, ninety-nine. In compound numbers, each non-zero three-digit group is followed by a scale word:

Value Common form
1,000 one thousand
1,000,000 one million
1,000,000,000 one billion
1,000,000,000,000 one trillion

The short scale shown above is standard in current US and UK English, but historical or translated documents may use different conventions. For an international contract, write the digits as well and define the scale if ambiguity is possible.

British and American Style

British usage commonly says one hundred and five; American style often says one hundred five. Both can be grammatical in their respective style guides. And is also commonly used between whole currency units and subunits, as in one dollar and twenty cents; it is not a universal requirement for checks.

Commas, capitalization, and hyphens are presentation choices that should be configured rather than hard-coded. A sentence usually uses lowercase; a form may capitalize the first word. Do not confuse an ordinal (twenty-first) with a cardinal (twenty-one).

Currency and Checks

Currency names and minor units are locale-specific. $ alone does not identify a currency in an international system, and “one fifty” is an informal US expression rather than a portable document format. Prefer an explicit form such as one US dollar and fifty cents when the context can cross borders.

Check layouts are governed by the issuing institution and jurisdiction. A US-style example may use One hundred twenty-five and 67/100 dollars, but another bank or country may require a different suffix, capitalization, or ordering. “Write a line after the amount” and “always use 00/100” are operational instructions only when the form provider requires them.

Chinese Number Rules

Ordinary Numerals and Financial Numerals

Ordinary Chinese writing uses characters such as , , and . Financial numerals use less easily confusable forms such as , , and . The latter are a document convention, not a cryptographic anti-tampering mechanism.

Arabic Ordinary Financial
0
1
2
3
4
5
6
7
8
9
10 / 100 / 1,000 十 / 百 / 千 拾 / 佰 / 仟

Chinese groups large numbers by four digits (, 亿) rather than by three. Zero is written only where it bridges a meaningful internal gap; exact placement depends on the group structure. A converter must be tested with values such as 10,001, 100,010, and 1,010,001, not just round hundreds.

Renminbi Amounts

For a common RMB financial representation, ¥1,234.56 may be written as 人民币壹仟贰佰叁拾肆元伍角陆分, and an exact integer amount as 人民币壹佰元整. /, the / ending, prefixes, and blank-filling rules can depend on the document and institution. Do not present one output as a universal People’s Bank, invoice, or cheque requirement without citing the applicable rule.

The converter must specify what happens to values with more than two decimal places. A robust default is to reject them and require the caller to apply an explicit, audited rounding policy first.

Exact Implementation Patterns

JavaScript: Arbitrary-Precision Integer Groups

This example accepts a signed integer string or bigint, uses groups of three, and supports a deliberately bounded scale table. It does not accept a JavaScript Number for large values.

javascript
const SMALL = [
  "zero", "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 underThousand(value, useAnd) {
  const n = Number(value);
  const parts = [];
  if (n >= 100) {
    parts.push(`${SMALL[Math.floor(n / 100)]} hundred`);
    value %= 100n;
    if (value > 0n && useAnd) parts.push("and");
  }
  if (value >= 20n) {
    parts.push(TENS[Math.floor(Number(value) / 10)]);
    if (value % 10n) parts[parts.length - 1] += `-${SMALL[Number(value % 10n)]}`;
  } else if (value > 0n) {
    parts.push(SMALL[Number(value)]);
  }
  return parts.join(" ");
}

export function integerToEnglish(input, { useAnd = false } = {}) {
  const text = typeof input === "bigint" ? input.toString() : String(input);
  if (!/^-?\d+$/.test(text)) throw new TypeError("expected an integer string");
  let value = BigInt(text);
  if (value === 0n) return "zero";
  const negative = value < 0n;
  if (negative) value = -value;

  const groups = [];
  while (value > 0n) {
    groups.push(value % 1000n);
    value /= 1000n;
  }
  if (groups.length > SCALES.length) throw new RangeError("scale is out of range");

  const words = [];
  for (let i = groups.length - 1; i >= 0; i--) {
    if (groups[i] === 0n) continue;
    const groupAnd = useAnd && i === 0 && words.length > 0;
    words.push(underThousand(groups[i], groupAnd));
    if (i > 0) words[words.length - 1] += ` ${SCALES[i]}`;
  }
  return `${negative ? "minus " : ""}${words.join(" ")}`;
}

console.log(integerToEnglish("1000000000001", { useAnd: true }));
// one trillion and one

The scale limit is a contract, not a mathematical limitation. Extend it with reviewed tests when the product needs larger values. For a user-facing locale, use a locale-aware library after checking its version and rules rather than assuming this English-only function covers every language.

Python: Integer Minor Units and Decimal

For money, accept an integer number of minor units when possible. If the input arrives as a decimal string, parse it with Decimal, require the expected exponent, and make rounding explicit:

python
from decimal import Decimal, InvalidOperation, ROUND_HALF_EVEN


def usd_to_minor_units(text: str, *, rounding=ROUND_HALF_EVEN) -> int:
    try:
        amount = Decimal(text)
    except InvalidOperation as exc:
        raise ValueError("amount must be a decimal string") from exc
    if not amount.is_finite():
        raise ValueError("amount must be finite")
    cents = (amount * 100).quantize(Decimal("1"), rounding=rounding)
    if cents != amount * 100:
        raise ValueError("amount was rounded; require an explicit policy")
    return int(cents)


def split_usd_minor_units(cents: int) -> tuple[int, int, bool]:
    if not isinstance(cents, int):
        raise TypeError("cents must be an integer")
    dollars, remainder = divmod(abs(cents), 100)
    return dollars, remainder, cents < 0

split_usd_minor_units returns exact components for a separately tested word renderer; it does not hide a rounding step or depend on an undefined helper. The snippet intentionally fails if quantization would change the supplied value. A system that wants half-even or half-up rounding should apply it at a documented boundary, record the original value and result, and test negative values, halfway cases, and currency-specific minor units.

Test the Contract, Not Just the Happy Path

At minimum, test 0, 19, 20, 21, 100, 101, 1,001, 1,000,001, negative values, the largest supported scale, malformed strings, leading signs, and values outside the declared range. For money, add 0.01, exact whole units, more-than-two-decimal input, negative amounts, and halfway rounding cases. Compare both numeric and written forms before a document is approved.

Accessibility, Localization, and Auditability

Use normal spaces and punctuation that copy correctly, and do not rely on color or a visual underline to distinguish a written amount from its numeric counterpart. Screen-reader users should receive the same value in an accessible text representation. RTL languages, non-breaking spaces, capitalization, and hyphenation require locale-specific tests.

For invoices, checks, and contracts, preserve the original numeric value, the conversion library and version, locale, currency, rounding mode, output, and validation result. A written amount alone does not prove who authorized a transaction or whether a document was altered.

Frequently Asked Questions

Does spelling out a number prevent fraud?

No. It can make manual comparison easier and may be required by a form, but it does not provide integrity or authorization. Use signatures, access controls, audit trails, and tamper-evident mechanisms where those properties are required.

Is “and” required in English?

No single rule applies everywhere. British and American style conventions differ, and check forms are governed by the issuer. Make the style an explicit configuration and verify it against the target document.

Can I pass a JavaScript Number to a money converter?

Only when the value is known to be exactly representable and within the documented range. Prefer an integer minor-unit string, bigint, or a decimal library for imported or high-value amounts.

Is a Chinese financial amount legally valid everywhere?

No. Financial numerals are a convention whose required prefix, unit, ending, and correction rules depend on the document and jurisdiction. Follow the issuing institution’s current instructions.

Should a converter silently round extra decimal places?

Usually not. Silent rounding can change the amount. Reject the value or apply a named rounding policy at a clearly audited boundary.

Conclusion

A reliable number-to-words feature starts with a precise contract, exact numeric handling, locale-aware language rules, and validation of both representations. Treat check and legal-document examples as jurisdiction-specific templates, not universal law. The safest implementation is the one that refuses ambiguity, records its assumptions, and lets the surrounding authorization and document workflow provide the controls that formatting cannot.

References