前端数字精度丢失原因和解决方案


前言

本地源码预览:https://gitcode.com/weixin_44862629/vue-study.git

  • 在项目开发中,一般比较重要的数值计算是交给后端处理,再返还给前端,(例如:订单金额、科学数据)。
  • 但在一些项目,前端不得不自己处理数值计算,这是就有可能遇到精度丢失
    例如:后端返回数字型id , 9999999999994322924 前端读取到的值 9999999999994323000

一、为什么会造成精度丢失?

1.1 超过最值

当数字超过-900719925474099 【+(2^53 – 1)】 或 9007199254740991时会出现精度丢失问题(后端返回数字id)

在这里插入图片描述

1.2 浮点数(javaScript数字存储规范)

在这里插入图片描述
关键字:IEEE 754规范53 位有效数字, 计算机二进制和十进制的互相编译

1、 JavaScript使用IEEE 754规范存储数字, 其规定双精度浮点数使用 64 位(8 字节) 来存储一个浮点数,可以表示二进位制的53 位有效数字。
2、在计算机语言中,其底层是对数据进行了二进制转换,存储的是二进制编码。

结论:在我们 输入\输出 数字时,其实进行了二进制和十进制的互相编译;这时某一个编译环节数据可能超过53位,同时IEEE 754规定了53 位有效数字。导致了浮点数精度丢失

二、精度不高的运算(小数据)

在处理浮点数计算且精度要求不高时,可以使用js自带数字处理方法

 // toFixed() 保留指定小数位数,返回字符串
let num = 3.14159;
let formattedNum = num.toFixed(2); // "3.14"

// parseFloat() 配合toFixed(),转回数字类型
Number.parseFloat(num.toFixed(2))	// 3.14

// 使用Math.round()、Math.floor()和Math.ceil()方法
let roundedNum = Math.round(num * 100) / 100; // 3.14 舍入到两位小数
let floorNum = Math.floor(num * 100) / 100; // 3.14  向下舍入到两位小数
let ceilNum = Math.ceil(num * 100) / 100; // 3.15,但只有在第三位小数是5或更大时才会真正向上舍入

// 利用正则表达式匹配小数位,移除多余的内容

三、化整计算(小数据)

对浮点数先扩大为整数,计算之后在还原倍数.

需要根据小数位数设置倍数;若接近16位数字,有可能出现超过最值精度丢失

/**
 * @param num1 数据1
 * @param num2 数据2
 * @param multiple 要扩大的倍数
 */
const computedNum = (num1: number, num2: number, multiple: number) => {
  return (num1 * multiple + num2 * multiple) / multiple;
};
console.log(computedNum(0.1, 0.2, 10)) //0.3

四、BigInt类型(Es6)(超过最值)

BigInt是JavaScript在ES2020中引入的一种新的基本数据类型,用于表示任意精度的整数
BigInt可以表示任意长度的整数,没有固定的上限和下限

4.1 BigInt声明

提示1:BigInt不是数字类型,不能使用处理数字的方法。下方组件也没有时使用【数字输入框】
提示2:BigInt只能和BigInt进行数学运算

const bigIntValue = BigInt(9999999999994322924);	// 方式一
const bigIntValue = 9999999999994322924n; // 方式二

截图中输入框使用的数据分别是:9999999999994322924、9999999999994322924n

在这里插入图片描述

<el-input /> 暂时不支持BigInt类型
在这里插入图片描述

<div class="mrg-b20">
  未进行处理:
  <el-input-number
    style="width: 350px"
    v-model="num5"
    controls-position="right"
  />
  = <el-input style="width: 350px" v-model="res3" />
</div>
<p style="color: tomato;"> 提示:下方是文本输入框,输入数字实际是字符串,数据在js绑定 </p>
<div>
  已进行BigInt处理:
  <el-input
    style="width: 350px"
    v-model="num6"
    :min="0"
    controls-position="right"
  />
  = {{ num6 }}
</div>
const num5 = ref(9999999999994322924);
const res3 = computed(() => {
  return num5.value;
});
const num6 = ref(9999999999994322924n);
console.log(num6.value); 	// 9999999999994322924n

五、第三方库(超过最值)

‌第三方库适用场景
Big.js‌能够处理高精度的浮点数运算。它提供了加、减、乘、除等基本运算功能,适用于大数和需要高精度浮点运算的场景‌
‌Decimal.js‌支持十进制浮点数运算。它能够处理复杂的金融计算,适用于需要高精度计算的场景‌
‌BigNumber.js‌提供了精确的数学运算功能,适用于各种需要高精度计算的场景
‌math.js‌不仅支持高精度计算,还提供了丰富的数学函数和常量,适用于复杂的数学计算需求‌
number-precision‌专门用于处理JavaScript计算精度的库,可以在Vue项目中引用,提供加减乘除以及四舍五入的相关方法‌

在这里插入图片描述

JSONbig.parse 是用于解析 JSON 字符串的,而不是直接用于处理 JavaScript 对象

 <div>
        已进行JSONbig处理:
        <el-input
          style="width: 350px"
          v-model="res5"
          :min="0"
          controls-position="right"
        />
</div>
import JSONbig from "json-bigint";
const jsonString = '{"num": 9999999999994322924}';
const res5 = computed(() => {
  return JSONbig.parse(jsonString).num;
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值