Skip to content

Composables

Decimal helpers ​

Exact arithmetic on decimal strings. Values are scaled to integers and computed with BigInt, so nothing passes through Number unless you ask it to. INumberInput is built on these.

+

addDecimals → 0.30

Number + Number → 0.30000000000000004

Binary floating point cannot represent most decimal fractions: 0.1 + 0.2 is not 0.3, and 10.00 becomes 10 as soon as it is a Number, losing the cents column.

The helpers ​

FunctionSignature
addDecimals(a: string, b: string) => string | undefinedExact addition; undefined if either side is invalid
compareDecimals(a: string, b: string) => number-1, 0, 1, or NaN for invalid input
roundDecimal(value: string, precision: number) => string | undefinedRound to a number of places
clampDecimal(value: string, min?: string, max?: string) => stringConstrain to a range
isDecimal(value: string) => booleanWhether the string is a canonical decimal
formatForLocale(value: string, locale: string, precision?: number) => stringFor display: grouping and the locale's separator
parseFromLocale(input: string, locale: string) => string | undefinedBack from what a reader typed
toEditable(value: string, locale?: string) => stringThe form a reader edits, without grouping
localeSeparators(locale: string) => { decimal, group }The separators a locale uses

Display and back ​

formatForLocale and parseFromLocale are a pair. The first is for showing a number — grouping separators, the locale's decimal mark. The second turns what someone typed back into a canonical decimal string, which is the only form the arithmetic accepts.

ts
formatForLocale('1234.5', 'de-DE', 2) // '1.234,50'
parseFromLocale('1.234,50', 'de-DE') // '1234.5'

Keep the canonical form in your model and the locale form only on screen.