Skip to content

金额计算

decimal.js(推荐)

ts
import Decimal from 'decimal.js'

export const subtraction = (a: number, b: number) => {
  const numberA = Number(a)
  const numberB = Number(b)
  if (isNaN(numberA) || isNaN(numberB)) throw new Error('参数必须是数字')

  return new Decimal(a).minus(new Decimal(b)).toNumber()
}

自己实现

js
function getDecimalLength(num: number): number {
  // 处理科学计数法
  const numStr = num.toString()
  if (numStr.includes('e')) {
    const [base, exponent] = numStr.split('e')
    const baseDecimals = base.includes('.') ? base.split('.')[1].length : 0
    return Math.max(0, baseDecimals - Number(exponent))
  }

  // 普通小数
  const decimal = numStr.split('.')[1]
  return decimal ? decimal.length : 0
}

/**
 * 减法
 */
export const subtraction = (a: number, b: number) => {
  const numberA = Number(a)
  const numberB = Number(b)
  if (isNaN(numberA) || isNaN(numberB)) throw new Error('参数必须是数字')

  const a_decimal = getDecimalLength(a)
  const b_decimal = getDecimalLength(b)
  const max_decimal = Math.max(a_decimal, b_decimal)

  // 检查是否会超出安全整数范围
  const maxSafeInteger = Number.MAX_SAFE_INTEGER
  const factor = 10 ** max_decimal

  if (Math.abs(a * factor) > maxSafeInteger || Math.abs(b * factor) > maxSafeInteger) {
    // 超出安全范围时使用字符串处理或抛出错误
    throw new Error('数字超出安全计算范围')
  }

  const a_plus = Math.round(a * factor) // 使用 Math.round 避免乘法精度问题
  const b_plus = Math.round(b * factor)

  return (a_plus - b_plus) / factor
}

/**
 * 加法
 */
export const addition = (a: number, b: number) => {
  const numberA = Number(a)
  const numberB = Number(b)
  if (isNaN(numberA) || isNaN(numberB)) throw new Error('参数必须是数字')

  const a_decimal = getDecimalLength(a)
  const b_decimal = getDecimalLength(b)
  const max_decimal = Math.max(a_decimal, b_decimal)

  // 检查是否会超出安全整数范围
  const maxSafeInteger = Number.MAX_SAFE_INTEGER
  const factor = 10 ** max_decimal

  if (Math.abs(a * factor) > maxSafeInteger || Math.abs(b * factor) > maxSafeInteger) {
    throw new Error('数字超出安全计算范围')
  }

  const a_plus = Math.round(a * factor)
  const b_plus = Math.round(b * factor)

  return (a_plus + b_plus) / factor
}

/**
 * 乘法
 */
export const multiplication = (a: number, b: number) => {
  const numberA = Number(a)
  const numberB = Number(b)
  if (isNaN(numberA) || isNaN(numberB)) throw new Error('参数必须是数字')

  const a_decimal = getDecimalLength(a)
  const b_decimal = getDecimalLength(b)
  const total_decimal = a_decimal + b_decimal

  // 检查是否会超出安全整数范围
  const maxSafeInteger = Number.MAX_SAFE_INTEGER
  const factor = 10 ** total_decimal

  if (Math.abs(a * factor) > maxSafeInteger || Math.abs(b * factor) > maxSafeInteger) {
    throw new Error('数字超出安全计算范围')
  }

  const a_plus = Math.round(a * 10 ** a_decimal)
  const b_plus = Math.round(b * 10 ** b_decimal)

  return (a_plus * b_plus) / factor
}

/**
 * 除法
 */
export const division = (a: number, b: number) => {
  const numberA = Number(a)
  const numberB = Number(b)
  if (isNaN(numberA) || isNaN(numberB)) throw new Error('参数必须是数字')
  if (numberB === 0) throw new Error('除数不能为0')

  const a_decimal = getDecimalLength(a)
  const b_decimal = getDecimalLength(b)
  const max_decimal = Math.max(a_decimal, b_decimal)

  // 检查是否会超出安全整数范围
  const maxSafeInteger = Number.MAX_SAFE_INTEGER
  const factor = 10 ** max_decimal

  if (Math.abs(a * factor) > maxSafeInteger || Math.abs(b * factor) > maxSafeInteger) {
    throw new Error('数字超出安全计算范围')
  }

  const a_plus = Math.round(a * factor)
  const b_plus = Math.round(b * factor)

  return a_plus / b_plus
}

/**
 * 数字计算工具类
 */
export class Calculator {
  private value: number

  constructor(initialValue: number = 0) {
    this.value = initialValue
  }

  /**
   * 实现 Symbol.toPrimitive 接口
   * 使得 Calculator 实例可以直接参与数值运算
   */
  [Symbol.toPrimitive]() {
    return this.value
  }

  /**
   * 快速创建计算器实例
   */
  static from(initialValue: number): Calculator {
    return new Calculator(initialValue)
  }

  /**
   * 快速计算: 加法
   */
  static add(a: number, b: number): number {
    return addition(a, b)
  }

  /**
   * 快速计算: 减法
   */
  static subtract(a: number, b: number): number {
    return subtraction(a, b)
  }

  /**
   * 快速计算: 乘法
   */
  static multiply(a: number, b: number): number {
    return multiplication(a, b)
  }

  /**
   * 快速计算: 除法
   */
  static divide(a: number, b: number): number {
    return division(a, b)
  }

  /**
   * 加法
   */
  add(num: number): Calculator {
    this.value = addition(this.value, num)
    return this
  }

  /**
   * 减法
   */
  subtract(num: number): Calculator {
    this.value = subtraction(this.value, num)
    return this
  }

  /**
   * 乘法
   */
  multiply(num: number): Calculator {
    this.value = multiplication(this.value, num)
    return this
  }

  /**
   * 除法
   */
  divide(num: number): Calculator {
    this.value = division(this.value, num)
    return this
  }

  /**
   * 获取结果
   */
  getValue(): number {
    return this.value
  }

  /**
   * 重置计算器
   */
  reset(num: number = 0): Calculator {
    this.value = num
    return this
  }
}