export interface BigIntFormatOptions { /** * The number of digits starting from the right of `value` to consider as decimal places. * * @default 18n */ precision?: bigint | undefined /** @default 0 */ minimumFractionDigits?: number | undefined /** @default 20 */ maximumFractionDigits?: number | undefined /** @default 'auto' */ trailingZeroDisplay?: 'auto' | 'stripIfInteger' | undefined } /** * Formats a bigint as a string. * * @param value - The bigint to format. * @param options - Formatting options. * @returns The string representation of `value` */ export function formatBigInt(value: bigint, options?: BigIntFormatOptions): string { const { precision = 18n, minimumFractionDigits = 0, maximumFractionDigits = 20, trailingZeroDisplay = 'auto', } = options ?? {} if (precision < 0n) { throw new Error(`[formatBigInt] \`precision\` must be positive, got ${precision}`) } if (minimumFractionDigits > maximumFractionDigits) { throw new Error( `[formatBigInt] \`minimumFractionDigits\` (${minimumFractionDigits}) cannot be greater than \`maximumFractionDigits\` (${maximumFractionDigits})`, ) } const isNegative = value < 0n const absValue = isNegative ? value * -1n : value const precisionFactor = 10n ** precision const integerPart = absValue / precisionFactor const integerPartString = `${isNegative ? '-' : ''}${integerPart.toLocaleString('en-US')}` const decimalPart = absValue % precisionFactor let decimalPartString = '' if ((decimalPart > 0n || minimumFractionDigits > 0) && maximumFractionDigits > 0) { decimalPartString = decimalPart .toLocaleString('en-US', { useGrouping: false, }) .padStart(Number(precision), '0') .replace(/0+$/, '') .padEnd(minimumFractionDigits, '0') .slice(0, maximumFractionDigits) if (trailingZeroDisplay === 'stripIfInteger' && decimalPartString.replace(/0/g, '') === '') { decimalPartString = '' } } return `${integerPartString}${decimalPartString ? `.${decimalPartString}` : ''}` }