export type Decimal = { value: bigint; scale: number }; export function decimalFromString(value: string): Decimal { if (!/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(value)) throw new Error('Invalid decimal string'); const negative = value.startsWith('-'); const [whole, fraction = ''] = (negative ? value.slice(1) : value).split('.'); return { value: (negative ? -1n : 1n) * BigInt(`${whole}${fraction}`), scale: fraction.length, }; } export function add(left: Decimal, right: Decimal): Decimal { const scale = Math.max(left.scale, right.scale); return { value: left.value * 10n ** BigInt(scale - left.scale) + right.value * 10n ** BigInt(scale - right.scale), scale, }; } export function subtract(left: Decimal, right: Decimal): Decimal { return add(left, { value: -right.value, scale: right.scale }); } export function multiply(left: Decimal, right: Decimal): Decimal { return { value: left.value * right.value, scale: left.scale + right.scale }; } export function isZero(value: Decimal): boolean { return value.value === 0n; } export function compare(left: Decimal, right: Decimal): number { const difference = subtract(left, right).value; return difference === 0n ? 0 : difference > 0n ? 1 : -1; } export function formatDecimal({ value, scale }: Decimal): string { if (value === 0n) return '0'; const sign = value < 0n ? '-' : ''; if (scale === 0) return `${sign}${value < 0n ? -value : value}`; const digits = (value < 0n ? -value : value) .toString() .padStart(scale + 1, '0'); const whole = digits.slice(0, -scale) || '0'; const fraction = digits.slice(-scale).replace(/0+$/, ''); return `${sign}${whole}${fraction ? `.${fraction}` : ''}`; } // Round half away from zero; callers choose presentation precision explicitly. export function divide(left: Decimal, right: Decimal, scale: number): Decimal { if (right.value === 0n) throw new Error('Division by zero'); const numerator = left.value * 10n ** BigInt(right.scale + scale); const denominator = right.value * 10n ** BigInt(left.scale); const sign = numerator < 0n !== denominator < 0n ? -1n : 1n; const n = numerator < 0n ? -numerator : numerator; const d = denominator < 0n ? -denominator : denominator; return { value: sign * ((n + d / 2n) / d), scale }; }