UNPKG

@0xtorch/big-decimal

Version:

An arbitrary precision Decimal type for TypeScript that extends BigInt.

111 lines (100 loc) 3.04 kB
import type { BigDecimal } from './type' export const createBigDecimal = ( value: string | number | bigint, decimals?: number, ): BigDecimal => { if (typeof value === 'string') { return createBigDecimalFromString(value, decimals) } if (typeof value === 'number') { return createBigDecimalFromString(value.toString(), decimals) } return createBigDecimalFromBigint(value, decimals) } const createBigDecimalFromString = ( value: string, decimals?: number, ): BigDecimal => { // e で分割 const splitedValue = value.replaceAll(' ', '').split(/[eE]/) const baseValue = splitedValue[0] const exponent: string | undefined = splitedValue[1] // マイナスかどうか const isNegative = baseValue.startsWith('-') const absText = isNegative ? baseValue.slice(1) : baseValue // '+' ',' '_' を除外する const numberText = absText .replaceAll('+', '') .replaceAll(',', '') .replaceAll('_', '') // . で文字列分割 // それぞれの block が数字のみで構成されていなかったらエラー const [intText, decimalText] = numberText.split('.') const numberRegExp = /^\d+$/ if ( !numberRegExp.test(intText) || (decimalText !== undefined && decimalText.length > 0 && !numberRegExp.test(decimalText)) ) { throw new Error(`Invalid decimal format value: ${baseValue}`) } // decimal 末尾の 0 は削除 (正規表現で良い感じに) const formattedDecimalText = decimalText !== undefined && decimalText.length > 0 ? decimalText.replace(/0+$/, '') : '' const formattedDecimals = formattedDecimalText.length // decimals の末尾を調整 const adjustedDecimalText = adjustDecimalText( decimals, formattedDecimalText, formattedDecimals, ) const valueBn = BigInt( `${isNegative ? '-' : ''}${intText}${adjustedDecimalText}`, ) const baseDecimals = decimals === undefined ? formattedDecimals : decimals // 指数がない場合はそのまま返す if (exponent === undefined) { return { value: valueBn, decimals: baseDecimals, } } // 指数がある場合は指数を考慮して返す const exponentNumber = Number(exponent) const decimalsWithExponent = baseDecimals - exponentNumber return decimalsWithExponent < 0 ? { value: valueBn * 10n ** BigInt(-decimalsWithExponent), decimals: 0, } : { value: valueBn, decimals: decimalsWithExponent, } } const adjustDecimalText = ( decimals: number | undefined, formattedDecimalText: string, formattedDecimals: number, ) => { if (decimals === undefined) { return formattedDecimalText } if (decimals > formattedDecimals) { return formattedDecimalText.padEnd(decimals, '0') } if (decimals < formattedDecimals) { return formattedDecimalText.slice(0, decimals) } return formattedDecimalText } const createBigDecimalFromBigint = ( value: bigint, decimals?: number, ): BigDecimal => ({ value, decimals: decimals ?? 0, })