javascript解决小数的加减乘除精度丢失的方案

作者:吴志春

续上篇 JavaScript的数字存储 我们知道了为何0.1+0.2≠0.3,发现了问题,那么这次我们来解决问题。

分析问题

在解决问题之前,我们先来弄清楚问题的根源。出现的问题的根源,整数是因为数据太大;小数是因为小数在转换成二进制时出现了无效循环的情况,由于存储位数限制因此存在“舍去”,精度丢失就发生了,。详细请看上一篇文章 JavaScript的数字存储

解决方案

对于整数,前端出现问题的几率可能比较低,毕竟很少有业务需要需要用到超大整数,只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。

对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。解决方式:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)。

例子

计算小数:0.1 + 0.02

计算方案:把小数放到位整数(乘倍数),再缩小回原来倍数(除倍数)。

计算过程

第一步把小数扩大成整数

0.1 扩大10倍 变成 1

0.02 扩大100倍 变成 2

第二步计算

1 * 100 ÷10 + 2 = 12

第三步缩小倍数(除以最大倍数)

12 ÷ 100 = 0.12

计算过程我们大概已经理清了,现在我们使用代码实现整个过程

代码实现

分析以上三个步骤,我们的代码实现分为两步。

第一步

为通过两个数字获取两个整数值,两个倍数值,以及最大倍数值。

例如:

入参为0.1 和0.02

则返回值应该是0.1的整数值1 ,倍数为10;0.02的整数值2,倍数值100。

代码实现如下

 /**
   * 判断num是否为一个整数
   * @param {number|string|null} value
   */
  isInteger = (value) => {
    if (isNaN(value)) return false;
    const num = +value;

    return Math.floor(num) === num;
  };  

  /**
   * 将一个浮点数转成整数,返回整数和倍数
   * @param {number|string|null} floatNum 如: 3.14
   * @returns {object} 如:{times:100, num: 314}
   */
toInteger = (floatNum) => {
    const ret = { times: 1, num: 0 };
    if (this.isInteger(floatNum)) {
      ret.num = floatNum;
      return ret;
    }
    const strfi = `${floatNum}`;
    const dotPos = strfi.indexOf('.');
    const len = strfi.substr(dotPos + 1).length;
    const times = 10 ** len;
    ret.times = times;
    ret.num = +strfi.replace('.', '');

    return ret;
  };

第二步,使用整数值以及倍数计算结果

代码如下

 add = (prevValue, nextValue) => {
    let result = null;
    //初始化
    const objectNumberA = this.toInteger(prevValue);
    const objectNumberB = this.toInteger(nextValue);
    const numberA = objectNumberA.num;
    const numberB = objectNumberB.num;
    const timesA = objectNumberA.times;
    const timesB = objectNumberB.times;
    const timesMax = timesA > timesB ? timesA : timesB;

    //计算加法
    if (timesA === timesB) {
      // 两个小数位长度相同
      result = numberA + numberB;
    } else if (timesA > timesB) {
      // numberA 小数位长度 大于 numberB
      result = numberA + numberB * (timesA / timesB);
    } else {
      // numberA 小数位长度 小于 numberB
      result = numberA * (timesB / timesA) + numberB;
    }

    //缩小并返回值
    return result / timesMax;
  };

代码测试

这样我们就小数加法丢失精度的问题。

同理我们可以实现减法,乘法,除法。具体实现代码入下,建议在有加法的基础上试试自己实现。

减法

 subtract = (prevValue, nextValue) => {
    let result = null;
    //初始化
    const objectNumberA = this.toInteger(prevValue);
    const objectNumberB = this.toInteger(nextValue);
    const numberA = objectNumberA.num;
    const numberB = objectNumberB.num;
    const timesA = objectNumberA.times;
    const timesB = objectNumberB.times;
    const timesMax = timesA > timesB ? timesA : timesB;
    
    //计算减法
    const { numberA, numberB, timesA, timesB, timesMax } = calcuNumber;
    if (timesA === timesB) {
      result = numberA - numberB;
    } else if (timesA > timesB) {
      result = numberA - numberB * (timesA / timesB);
    } else {
      result = numberA * (timesB / timesA) - numberB;
    }
    
		//缩小并输出
    return result / timesMax;
  };

乘法

 multiply = (prevValue, nextValue) => {
   //初始化 
   	const objectNumberA = this.toInteger(prevValue);
    const objectNumberB = this.toInteger(nextValue);
    const numberA = objectNumberA.num;
    const numberB = objectNumberB.num;
    const timesA = objectNumberA.times;
    const timesB = objectNumberB.times;
    const timesMax = timesA > timesB ? timesA : timesB;
    
		//计算乘法,缩小并输出
    return (numberA * numberB) / (timesA * timesB);
  };

除法

divide = (prevValue, nextValue) => {
    //初始化 
   	const objectNumberA = this.toInteger(prevValue);
    const objectNumberB = this.toInteger(nextValue);
    const numberA = objectNumberA.num;
    const numberB = objectNumberB.num;
    const timesA = objectNumberA.times;
    const timesB = objectNumberB.times;
    const timesMax = timesA > timesB ? timesA : timesB;
    
		//计算除法,缩小并输出
    return (numberA / numberB) * (timesB / timesA);
  };

我们发现初始化代码会有重复,我们可以抽出来做成共用方法,这一步交给你们了。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
在Vue中,可以使用第三方库Decimal.js来解决小数的四则运算导致精度丢失的问题。Decimal.js是一个用于高精度数值计算的JavaScript库。 首先,需要在Vue项目中通过npm安装Decimal.js库。可以使用以下命令进行安装: ``` npm install decimal.js --save ``` 安装完成后,在需要使用的组件中引入Decimal.js库: ``` import Decimal from 'decimal.js'; ``` 然后,就可以使用Decimal.js库中提供的方法进行小数的四则运算了。 下面是一个示例代码,演示如何使用Decimal.js进行加减乘除运算并避免精度丢失的问题: ```vue <template> <div> <p>加法运算结果:{{ addResult }}</p> <p>减法运算结果:{{ subResult }}</p> <p>乘法运算结果:{{ mulResult }}</p> <p>除法运算结果:{{ divResult }}</p> </div> </template> <script> import Decimal from 'decimal.js'; export default { data() { return { addResult: 0, subResult: 0, mulResult: 0, divResult: 0, }; }, mounted() { // 加法运算 this.addResult = new Decimal(0.1).plus(0.2).toNumber(); // 减法运算 this.subResult = new Decimal(0.3).minus(0.1).toNumber(); // 乘法运算 this.mulResult = new Decimal(0.1).times(0.2).toNumber(); // 除法运算 this.divResult = new Decimal(0.3).dividedBy(0.1).toNumber(); }, }; </script> ``` 在上述示例代码中,首先通过`import`语句引入了Decimal.js库。然后,通过`new Decimal()`创建Decimal对象,并使用Decimal对象的方法进行加减乘除运算。最后,使用`toNumber()`方法将结果转换为普通的JavaScript数字类型,并将结果渲染到模板中。 通过使用Decimal.js库,可以避免小数四则运算导致的精度丢失问题,保证计算结果的准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值