js计算精度丢失(decimal.js),去小数位

项目场景:

提示:计算值精度丢失(decimal.js),取小数位


原因:

JS 数字丢失精度的原因:

JavaScript 中所有数字包括整数和小数都只有一种类型 — Number。它的实现遵循 IEEE 754 标准,使用 64 位固定长度来表示,也就是标准的 double 双精度浮点数(相关的还有float 32位单精度)。为什么呢,因为这样节省存储空间。

也就是说 17652.19 + 7673.78 = 25325.969999999998。其实最简单的例子是 0.1+0.2 = 0.30000000000000004

0.1的二进制表示的是一个无限循环小数,该版本的 JS 采用的是浮点数标准需要对这种无限循环的二进制进行截取,从而导致了精度丢失,造成了0.1不再是0.1,截取之后0.1变成了 0.100…001,0.2变成了0.200…002。所以两者相加的数大于0.3。

将0.1转换成为二进制加上0.2的二进制会是53位,但是二进制的最大位数是52位取近似值。

解决方案:


1、使用Math.floor()地板函数–舍弃小数部分取整数
0.1*0.2精度丢失
在这里插入图片描述
将数值转化成整数再进行相乘

multiply = (num1,num2) => {
  let m = 0,
    n1 = num1.toString(),
    n2 = num2.toString();
  //取两数小数位进行相加
    m += n1.split('.')[1].length;
    m += n2.split('.')[1].length;
    //将数值用replace将小数点去除,进行取整再除以10的 m次方
  return (Number(n1.replace('.', '')) * Number(n2.replace('.', ''))) / Math.pow(10, m);
};

2、去量位小数(特殊情况下会丢失精度)
Math.floor()

Math.floor(0.1*0.2*100)/100

特殊情况:
在这里插入图片描述
在这里插入图片描述

toFixed(2),取两位小数(这里根据不同浏览器会有兼容问题)会进行银行家舍入发法

银行家舍入:所谓银行家舍入法,其实质是一种四舍六入五取偶(又称四舍六入五留双)法。

简单来说就是:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

chrome上的测试结果:

1.35.toFixed(1) // 1.4 正确

1.335.toFixed(2) // 1.33 错误

1.3335.toFixed(3) // 1.333 错误

1.33335.toFixed(4) // 1.3334 正确

1.333335.toFixed(5)  // 1.33333 错误

1.3333335.toFixed(6) // 1.333333 错误

3.转字符串,利用字符串下标进行截取substring

/**
 * 取2位小数(可自定义)
 */
export const multiply = (num1, num2) => {
  let sum = ''
  //1、toFixed(3),利用它去取得小数后3位(可自定义--如果去问位就toFixed(4))
  //2、如果sum是整数的话  sum.indexOf(".") 会失效报错,那么就需要用toFixed(3)进行处理,
  //例如 10.toFixed(3)=10.000
  sum =(parseFloat(num1)*parseFloat(num2)).toFixed(3).toString()
  return +sum.substring(0, sum.indexOf(".") + 3)
 
}

4.第三方:
decimal.js是使用的二进制来计算的, 所以能解决js的精度问题

npm install --save decimal.js  // 安装
import Decimal from "decimal.js"  // 具体文件中引入

计算:

let a = 1
let b = 6 
//加
let res = new Decimal(a).add(new Decimal(b))  //得到的值是一个Decimal对象 需要转换
let res1 = new Decimal(a).add(new Decimal(b)).toNumber() //结果转换成number
let res2 = new Decimal(a).add(new Decimal(b)).toString() //结果转换成string
//下面同上操作
//减
let res = new Decimal(a).sub(new Decimal(b))
//乘
let res = new Decimal(a).mul(new Decimal(b))
//除
let res = new Decimal(a).div(new Decimal(b))

固定取两位小数,其他抹去

/**
 * 取2位小数(可自定义)
 * 
 * @param num1 参数1
 * @param num2 参数2
 * @param status 1(+) 2(-) 3(*)
 * @param num 小数后 num-1位
 * @returns 
 */
export const multiply = (num1, num2, status,num=3) => {
  let sum = ''
  if (status === 1) {
    sum = new Decimal(parseFloat(num1)).add(new Decimal(parseFloat(num2))).toFixed(3).toString()
    return +sum.substring(0, sum.indexOf(".") + 3)
  } else if (status === 3) {
    sum = new Decimal(parseFloat(num1)).mul(new Decimal(parseFloat(num2))).toFixed(3).toString()

    return +sum.substring(0, sum.indexOf(".") + 3)
  }
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值