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

一、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))
相关推荐
stringwu3 小时前
Flutter 中的 MVVM 架构实现指南
前端·flutter
俩毛豆3 小时前
【页面路由导航】三步实现页面跳转的完整示例
前端·harmonyos
Happy coder3 小时前
【avalonia教程】17mvvm简介、command
前端·javascript·vue.js
喵叔哟3 小时前
9. 从0到上线:.NET 8 + ML.NET LTR 智能类目匹配实战--Web API 接口与前端集成:把能力对外开放
前端·.net
烟袅3 小时前
CSS Animation 全面解析:从入门到实战,打造丝滑动效
前端·css
前端西瓜哥4 小时前
平面几何:多边线光滑化处理
前端
老前端的功夫4 小时前
Webpack 优化:你的构建速度其实还能快10倍
前端·javascript
Holin_浩霖4 小时前
React渲染原理学习笔记
前端
OpenTiny社区4 小时前
我用3 分钟上手 RankProcessChart 排名进度图!
前端·github