前端解决两数计算精度确实问题

一、1. 生产环境推荐使用 decimal.js - 功能完整,性能良好

使用第三方数学计算库( decimal.js)

csharp 复制代码
npm install decimal.js

封装计算方法

typescript 复制代码
import Decimal from "decimal.js-light"

// 内部工具:安全生成 Decimal
function toDecimal(value: string | number | null | undefined): Decimal {
	if (value === null || value === undefined || value === "" || isNaN(Number(value))) {
		return new Decimal(0)
	}
	return new Decimal(value)
}

// 加
export function decimal_add(x: string | number, y: string | number): number {
	return toDecimal(x).plus(toDecimal(y)).toNumber()
}

// 减
export function decimal_sub(x: string | number, y: string | number): number {
	return toDecimal(x).sub(toDecimal(y)).toNumber()
}

// 除
export function decimal_div(x: string | number, y: string | number): number {
	const divisor = toDecimal(y)
	if (divisor.isZero()) return 0 // 避免除 0
	return toDecimal(x).div(divisor).toNumber()
}

// 乘
export function decimal_mul(x: string | number, y: string | number): number {
	return toDecimal(x).mul(toDecimal(y)).toNumber()
}

or直接使用

xml 复制代码
<template>
  <div>
    <input v-model.number="num1" type="number" />
    <input v-model.number="num2" type="number" />
    <button @click="calculate">计算</button>
    <p>结果:{{ result }}</p>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { Decimal } from 'decimal.js'

const num1 = ref(0.1)
const num2 = ref(0.2)
const result = ref(0)

const calculate = () => {
  // 使用 Decimal 进行精确计算
  const a = new Decimal(num1.value)
  const b = new Decimal(num2.value)
  
  // 加法
  result.value = a.plus(b).toNumber()
  
  // 其他运算
  // const subtract = a.minus(b).toNumber()  // 减法
  // const multiply = a.times(b).toNumber()  // 乘法
  // const divide = a.dividedBy(b).toNumber() // 除法
}
</script>

二、使用big.js

复制代码
npm install big.js
javascript 复制代码
// utils/bigCalculator.js
import Big from 'big.js'

/**
 * 精确加法
 * @param {number|string} a - 第一个数
 * @param {number|string} b - 第二个数
 * @param {number} [precision] - 精度(小数位数)
 * @returns {number} 计算结果
 */
export function add(a, b, precision) {
  try {
    const result = new Big(a).plus(new Big(b))
    return precision !== undefined ? round(result, precision) : result.toNumber()
  } catch (error) {
    console.error('加法计算错误:', error)
    return NaN
  }
}

/**
 * 精确减法
 * @param {number|string} a - 被减数
 * @param {number|string} b - 减数
 * @param {number} [precision] - 精度(小数位数)
 * @returns {number} 计算结果
 */
export function subtract(a, b, precision) {
  try {
    const result = new Big(a).minus(new Big(b))
    return precision !== undefined ? round(result, precision) : result.toNumber()
  } catch (error) {
    console.error('减法计算错误:', error)
    return NaN
  }
}

/**
 * 精确乘法
 * @param {number|string} a - 第一个数
 * @param {number|string} b - 第二个数
 * @param {number} [precision] - 精度(小数位数)
 * @returns {number} 计算结果
 */
export function multiply(a, b, precision) {
  try {
    const result = new Big(a).times(new Big(b))
    return precision !== undefined ? round(result, precision) : result.toNumber()
  } catch (error) {
    console.error('乘法计算错误:', error)
    return NaN
  }
}

/**
 * 精确除法
 * @param {number|string} a - 被除数
 * @param {number|string} b - 除数
 * @param {number} [precision] - 精度(小数位数)
 * @returns {number} 计算结果
 */
export function divide(a, b, precision) {
  try {
    if (new Big(b).eq(0)) {
      throw new Error('除数不能为零')
    }
    const result = new Big(a).div(new Big(b))
    return precision !== undefined ? round(result, precision) : result.toNumber()
  } catch (error) {
    console.error('除法计算错误:', error)
    return NaN
  }
}

/**
 * 四舍五入
 * @param {Big} value - big.js 对象
 * @param {number} precision - 小数位数
 * @returns {number} 四舍五入后的值
 */
function round(value, precision) {
  return Number(value.toFixed(precision))
}

/**
 * 链式计算器
 */
export class Calculator {
  constructor(initialValue = 0) {
    this.value = new Big(initialValue)
  }

  add(num) {
    this.value = this.value.plus(new Big(num))
    return this
  }

  subtract(num) {
    this.value = this.value.minus(new Big(num))
    return this
  }

  multiply(num) {
    this.value = this.value.times(new Big(num))
    return this
  }

  divide(num) {
    if (new Big(num).eq(0)) {
      throw new Error('除数不能为零')
    }
    this.value = this.value.div(new Big(num))
    return this
  }

  result(precision) {
    return precision !== undefined 
      ? Number(this.value.toFixed(precision))
      : this.value.toNumber()
  }
}

三、 简单场景可使用自定义工具函数 - 减少依赖

1. 基础版本工具函数

php 复制代码
// utils/precisionCalculator.js

/**
 * 获取数字的小数位数
 * @param {number|string} num - 数字
 * @returns {number} 小数位数
 */
function getDecimalPlaces(num) {
  const numStr = num.toString()
  const decimalIndex = numStr.indexOf('.')
  return decimalIndex === -1 ? 0 : numStr.length - decimalIndex - 1
}

/**
 * 获取多个数字中最大的小数位数
 * @param {...number} numbers - 数字
 * @returns {number} 最大小数位数
 */
function getMaxDecimalPlaces(...numbers) {
  return Math.max(...numbers.map(num => getDecimalPlaces(num)))
}

/**
 * 将数字转换为整数(放大倍数)
 * @param {number} num - 数字
 * @param {number} decimalPlaces - 小数位数
 * @returns {number} 整数
 */
function toInteger(num, decimalPlaces) {
  return Math.round(num * Math.pow(10, decimalPlaces))
}

/**
 * 精确加法
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @param {number} [precision] - 精度(保留小数位数)
 * @returns {number} 计算结果
 */
export function add(a, b, precision) {
  const maxDecimal = getMaxDecimalPlaces(a, b)
  const multiplier = Math.pow(10, maxDecimal)
  
  const result = (toInteger(a, maxDecimal) + toInteger(b, maxDecimal)) / multiplier
  
  return precision !== undefined ? round(result, precision) : result
}

/**
 * 精确减法
 * @param {number} a - 被减数
 * @param {number} b - 减数
 * @param {number} [precision] - 精度(保留小数位数)
 * @returns {number} 计算结果
 */
export function subtract(a, b, precision) {
  const maxDecimal = getMaxDecimalPlaces(a, b)
  const multiplier = Math.pow(10, maxDecimal)
  
  const result = (toInteger(a, maxDecimal) - toInteger(b, maxDecimal)) / multiplier
  
  return precision !== undefined ? round(result, precision) : result
}

/**
 * 精确乘法
 * @param {number} a - 第一个数
 * @param {number} b - 第二个数
 * @param {number} [precision] - 精度(保留小数位数)
 * @returns {number} 计算结果
 */
export function multiply(a, b, precision) {
  const aDecimal = getDecimalPlaces(a)
  const bDecimal = getDecimalPlaces(b)
  const totalDecimal = aDecimal + bDecimal
  
  const aInt = toInteger(a, aDecimal)
  const bInt = toInteger(b, bDecimal)
  
  const result = (aInt * bInt) / Math.pow(10, totalDecimal)
  
  return precision !== undefined ? round(result, precision) : result
}

/**
 * 精确除法
 * @param {number} a - 被除数
 * @param {number} b - 除数
 * @param {number} [precision] - 精度(保留小数位数)
 * @returns {number} 计算结果
 */
export function divide(a, b, precision) {
  if (b === 0) {
    throw new Error('除数不能为零')
  }
  
  const aDecimal = getDecimalPlaces(a)
  const bDecimal = getDecimalPlaces(b)
  const maxDecimal = Math.max(aDecimal, bDecimal)
  
  // 将被除数和除数都转换为整数
  const aInt = toInteger(a, maxDecimal)
  const bInt = toInteger(b, maxDecimal)
  
  // 增加精度,避免除不尽的情况
  const extraPrecision = precision !== undefined ? precision : 8
  const result = (aInt / bInt) * Math.pow(10, maxDecimal - maxDecimal)
  
  return precision !== undefined ? round(result, precision) : result
}

/**
 * 四舍五入
 * @param {number} num - 数字
 * @param {number} precision - 小数位数
 * @returns {number} 四舍五入后的值
 */
export function round(num, precision) {
  const multiplier = Math.pow(10, precision)
  return Math.round(num * multiplier) / multiplier
}

2. 增强版本工具函数

javascript 复制代码
// utils/advancedPrecisionCalculator.js

import { add, subtract, multiply, divide, round } from './precisionCalculator'

/**
 * 高级精度计算工具
 */
export const precisionCalculator = {
  /**
   * 多个数字相加
   * @param {...number} numbers - 要相加的数字
   * @param {number} [precision] - 精度
   * @returns {number} 总和
   */
  sum(numbers, precision) {
    if (!Array.isArray(numbers)) {
      numbers = Array.from(arguments)
      precision = numbers[numbers.length - 1]
      if (typeof precision === 'number') {
        numbers = numbers.slice(0, -1)
      } else {
        precision = undefined
      }
    }
    
    return numbers.reduce((acc, curr) => add(acc, curr), precision !== undefined ? round(0, precision) : 0)
  },

  /**
   * 多个数字相乘
   * @param {...number} numbers - 要相乘的数字
   * @param {number} [precision] - 精度
   * @returns {number} 乘积
   */
  product(numbers, precision) {
    if (!Array.isArray(numbers)) {
      numbers = Array.from(arguments)
      precision = numbers[numbers.length - 1]
      if (typeof precision === 'number') {
        numbers = numbers.slice(0, -1)
      } else {
        precision = undefined
      }
    }
    
    return numbers.reduce((acc, curr) => multiply(acc, curr), precision !== undefined ? round(1, precision) : 1)
  },

  /**
   * 计算平均值
   * @param {...number} numbers - 数字
   * @param {number} [precision] - 精度
   * @returns {number} 平均值
   */
  average(numbers, precision) {
    if (!Array.isArray(numbers)) {
      numbers = Array.from(arguments)
      precision = numbers[numbers.length - 1]
      if (typeof precision === 'number') {
        numbers = numbers.slice(0, -1)
      } else {
        precision = undefined
      }
    }
    
    const sum = this.sum(numbers)
    const avg = divide(sum, numbers.length)
    
    return precision !== undefined ? round(avg, precision) : avg
  },

  /**
   * 链式计算器
   */
  createCalculator(initialValue = 0) {
    let value = initialValue
    
    return {
      add(num) {
        value = add(value, num)
        return this
      },
      
      subtract(num) {
        value = subtract(value, num)
        return this
      },
      
      multiply(num) {
        value = multiply(value, num)
        return this
      },
      
      divide(num) {
        value = divide(value, num)
        return this
      },
      
      round(precision) {
        value = round(value, precision)
        return this
      },
      
      value() {
        return value
      },
      
      toNumber() {
        return value
      }
    }
  },

  /**
   * 比较两个数字是否相等(考虑精度误差)
   * @param {number} a - 第一个数字
   * @param {number} b - 第二个数字
   * @param {number} tolerance - 容差
   * @returns {boolean} 是否相等
   */
  isEqual(a, b, tolerance = 1e-10) {
    return Math.abs(subtract(a, b)) < tolerance
  },

  /**
   * 格式化数字为字符串,避免科学计数法
   * @param {number} num - 数字
   * @param {number} precision - 小数位数
   * @returns {string} 格式化后的字符串
   */
  format(num, precision) {
    const value = precision !== undefined ? round(num, precision) : num
    return value.toString()
  }
}

// 导出基础函数
export { add, subtract, multiply, divide, round }

在组件中使用

scss 复制代码
const { 
  add, 
  subtract, 
  multiply, 
  divide,
  sum,
  product,
  average,
  createCalculator
} = usePrecisionCalculator()

// 基础计算
const baseA = ref(0.1)
const baseB = ref(0.2)
const baseOperation = ref('add')
const basePrecision = ref(2)

const normalBaseResult = computed(() => {
  const a = baseA.value
  const b = baseB.value
  
  switch (baseOperation.value) {
    case 'add': return a + b
    case 'subtract': return a - b
    case 'multiply': return a * b
    case 'divide': return a / b
    default: return 0
  }
})

const preciseBaseResult = computed(() => {
  const a = baseA.value
  const b = baseB.value
  const precision = basePrecision.value
  
  let result
  switch (baseOperation.value) {
    case 'add': result = add(a, b); break
    case 'subtract': result = subtract(a, b); break
    case 'multiply': result = multiply(a, b); break
    case 'divide': result = divide(a, b); break
    default: result = 0
  }
  
  return precision !== undefined ? round(result, precision) : result
})

const showBaseError = computed(() => {
  return Math.abs(normalBaseResult.value - preciseBaseResult.value) > 1e-10
})

// 多个数字计算
const numberList = ref('0.1, 0.2, 0.3')
const multiPrecision = ref(2)

const numberArray = computed(() => {
  return numberList.value.split(',')
    .map(num => num.trim())
    .filter(num => num !== '')
    .map(Number)
})

const sumResult = computed(() => 
  sum(numberArray.value, multiPrecision.value)
)

const productResult = computed(() => 
  product(numberArray.value, multiPrecision.value)
)

const averageResult = computed(() => 
  average(numberArray.value, multiPrecision.value)
)

// 链式计算
const chainResult = computed(() => 
  createCalculator(10)
    .add(0.1)
    .multiply(2)
    .subtract(0.2)
    .divide(3)
    .round(4)
    .value()
)

// 精度测试
const test1 = computed(() => add(0.1, 0.2))
const test2 = computed(() => subtract(0.3, 0.1))
const test3 = computed(() => multiply(0.1, 0.2))
const test4 = computed(() => divide(0.3, 0.1))
相关推荐
崔庆才丨静觅2 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax