iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Numbers

Numeric regex covers a lot of everyday validation: integers, decimals, currency, signed values, scientific notation, percentages. The trick is matching the SHAPE you actually want and parsing real numbers with the language’s built-in (parseFloat, Number) once shape is confirmed.

Integer, decimal, currency, scientific

EXAMPLE
// 1) Whole non-negative integer
const INT_RE = /^\d+$/;
INT_RE.test('0');         // true
INT_RE.test('123');       // true
INT_RE.test('-1');        // false
INT_RE.test('1.5');       // false
INT_RE.test('');          // false

// 2) Optional sign + integer
const SIGNED_INT_RE = /^[+-]?\d+$/;
SIGNED_INT_RE.test('-42');    // true
SIGNED_INT_RE.test('+0');     // true

// 3) Decimal number — integer or with fractional part
const DECIMAL_RE = /^[+-]?\d+(?:\.\d+)?$/;
DECIMAL_RE.test('3.14');     // true
DECIMAL_RE.test('-0.5');     // true
DECIMAL_RE.test('.5');       // false (no leading digit)
DECIMAL_RE.test('3.');       // false (no trailing digit)

// More forgiving — leading or trailing optional
const FLEX_DECIMAL = /^[+-]?(?:\d+\.?\d*|\.\d+)$/;
FLEX_DECIMAL.test('3.14');
FLEX_DECIMAL.test('.5');
FLEX_DECIMAL.test('3.');

// 4) Currency — 2 decimal places, optional commas, optional sign
const USD_RE = /^[+-]?\\$?\d{1,3}(?:,\d{3})*(?:\.\d{1,2})?$/;
USD_RE.test('$1,234.56');     // true
USD_RE.test('1234.56');         // true
USD_RE.test('1234');            // true
USD_RE.test('1,234');           // true
USD_RE.test('1,2');             // false

// CAVEAT: locales differ. Europe uses '1.234,56'; better to strip + use Intl.NumberFormat to parse.

// 5) Percentage
const PCT_RE = /^[+-]?\d+(?:\.\d+)?%$/;
PCT_RE.test('25%');             // true
PCT_RE.test('-3.5%');           // true

// 6) Scientific notation
const SCI_RE = /^[+-]?\d+(?:\.\d+)?[eE][+-]?\d+$/;
SCI_RE.test('1.5e3');           // true
SCI_RE.test('-2.5E-10');         // true
SCI_RE.test('1e0');              // true

// 7) Hex / binary / octal
const HEX_RE = /^0x[0-9a-fA-F]+$/;
const BIN_RE = /^0b[01]+$/;
const OCT_RE = /^0o[0-7]+$/;
HEX_RE.test('0xff');             // true
BIN_RE.test('0b1010');           // true

// 8) Integer range
const BYTE_RE = /^(?:25[0-5]|2[0-4]\d|1?\d?\d)$/;
BYTE_RE.test('255');             // true
BYTE_RE.test('256');             // false

// IPv4 octet — combine 4x:
const IPV4_RE = /^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)$/;

// 9) Phone number digits only (10-15 digits, optional +)
const PHONE_RE = /^\+?\d{10,15}$/;
PHONE_RE.test('+61412345678');   // true
PHONE_RE.test('0412345678');     // false (no + but still digits) — adjust if leading 0 allowed

// 10) Fixed-precision (e.g. 8-digit ID)
const ID_RE = /^\d{8}$/;
ID_RE.test('12345678');          // true
ID_RE.test('1234567');           // false (7 digits)

// 11) Numbers with thousands separators
const SEP_NUM = /^\d{1,3}(?:,\d{3})*(?:\.\d+)?$/;
SEP_NUM.test('1,234,567.89');    // true
SEP_NUM.test('1234567.89');      // false (no commas — write a forgiving pattern)

// More forgiving — accept either
const FLEX_NUM = /^\d{1,3}(?:[,]\d{3})*(?:\.\d+)?$|^\d+(?:\.\d+)?$/;
FLEX_NUM.test('1234567.89');     // true
FLEX_NUM.test('1,234,567.89');   // true

// 12) Real-world: validate + parse
function parseAmount(input) {
    const cleaned = input.replace(/[$,\s]/g, '');
    if (!FLEX_DECIMAL.test(cleaned)) return null;
    const n = parseFloat(cleaned);
    return Number.isFinite(n) ? n : null;
}

parseAmount('$1,234.56');      // 1234.56
parseAmount('1234');             // 1234
parseAmount('abc');              // null

// 13) Avoid relying on regex for safety on financial math
// • JavaScript number precision: 0.1 + 0.2 !== 0.3
// • Use BigInt for cents: 100 * dollars + cents → BigInt
// • Use libraries like dinero.js or money.js for arithmetic

const dollarsCents = '1234.56';
const cents = BigInt(dollarsCents.replace('.', ''));    // 123456n — safe big integer

// 14) Locale-aware parsing — better than regex for user input
const formatter = new Intl.NumberFormat('en-AU', { style: 'currency', currency: 'AUD' });
const groupingChar = (new Intl.NumberFormat('en-AU').formatToParts(1000).find((p) => p.type === 'group')?.value ?? ',');
const decimalChar = (new Intl.NumberFormat('en-AU').formatToParts(0.1).find((p) => p.type === 'decimal')?.value ?? '.');

function parseLocalised(input) {
    const clean = input.replace(new RegExp(`[\\\\\\\\\$\\\\\\\${groupingChar}]`, 'g'), '').replace(decimalChar, '.');
    return parseFloat(clean);
}

parseLocalised('1,234.56');     // 1234.56 (en-AU)
parseLocalised('1.234,56');     // works in de-DE configuration

// 15) When regex is the right tool
// • Quick shape check (UI form validation)
// • Extract numbers from free text ('total: $42.99 ' → ['$42.99'])
// • Log parsing (counts, durations)
//
// When regex is the WRONG tool
// • Real money math — use proper decimal/BigInt libraries
// • Cross-locale parsing — use Intl
// • Rigorous range validation — combine regex with numeric checks (Number.isFinite, etc.)

// 16) Common bugs
// • Forgetting ^ and $ → '\d+' matches 'foo123bar' as 123
// • Allowing '.' alone → use the FLEX_DECIMAL pattern
// • Catastrophic backtracking with nested quantifiers — keep patterns simple
// • Trusting regex for range (1-100) — match digits then test numerically
// • Negative numbers without explicit sign group → false negative
// • Localisation: '1,234.56' (en-US) vs '1.234,56' (de-DE) — strip and re-parse with Intl
// • Scientific notation skipped → some users expect '1e3'; document or accept
// • Leading zeros (012345) interpreted as octal in some parsers — use parseInt(s, 10) explicitly

Why it matters

Use regex for shape (^\d+$, ^[+-]?\d+(?:\.\d+)?$, etc.) and a real numeric parser for value — then range-check numerically. For currency and financial math, stay in BigInt cents and reach for Intl.NumberFormat for locale-aware parsing — regex alone can’t handle European decimal commas.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/   // signed decimal with optional exponent
Try it Yourself »

Discussion

Loading…