js精度丢失的问题

1.js精度丢失的常见问题,从常见的浮点型进行计算,到位数很长的munber类型进行计算都会造成精度丢失的问题,

首先我们看一个问题:

复制代码
0.1 + 0.2  !==  0.3  // true

let a =  9007199254740992
a + 1 == a // true

那么js为什么会出现精度丢失的问题:

计算机的二进制实现和位数限制有些数无法有限表示。就像一些无理数不能有限表示,如 圆周率 3.1415926...,1.3333... 等。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit

比如:

0.1 >> 0.0001 1001 1001 1001...(1001无限循环)

0.2 >> 0.0011 0011 0011 0011...(0011无限循环)

此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。

大整数的精度丢失和浮点数本质上是一样的,尾数位最大是52位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。

大于 9007199254740992 的可能会丢失精度

9007199254740992 >> 10000000000000...000 // 共计 53 个 0

9007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0

9007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0

当你做如下计算的时候

9007199254740992 + 1 // 丢失

9007199254740992 + 2 // 未丢失

9007199254740992 + 3 // 丢失

9007199254740992 + 4 // 未丢失

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

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

// 0.1 + 0.2

(0.110 + 0.210) / 10 == 0.3 // true

对于过大的数字:

可以用bigint,应为number的基本类型不能超过2^53,不然就会出现精度丢失,为了解决这个限制,在ECMAScript标准中出现了BigInt。

BigInt可以表示任意大的整数

但是要注意写法:

复制代码
let result=124569875984123677888999;  //估摸着这一步就在前端已经精度丢失了
String(BigInt(result));

let result=BigInt(124569875984123677888999);   //这里也已经精度丢失了
String(result);
// 上面两种都是错误的写法

// 正确写法:
let result=124569875984123677888999n; 
String(result);

除了以上方式,还可以借助第三方库

BigNumber.js:提供了超高精度的数字处理能力,可以解决精度丢失问题。

decimal.js:提供了类似于 Python Decimal 的类型,可以精确表示浮点数,解决精度丢失问题。

复制代码
npm install bignumber.js --save

let x = new BigNumber(123.4567);
let y = BigNumber('123456.7e-3');
let z = new BigNumber(x);
x.isEqualTo(y) && y.isEqualTo(z) && x.isEqualTo(z);      // true

let x = new BigNumber('1111222233334444555566');
x.toString();                       // "1.111222233334444555566e+21"
x.toFixed();                        // "1111222233334444555566"

// Precision loss from using numeric literals with more than 15 significant digits.
new BigNumber(1.0000000000000001)         // '1'
new BigNumber(88259496234518.57)          // '88259496234518.56'
new BigNumber(99999999999999999999)       // '100000000000000000000'

// Precision loss from using numeric literals outside the range of Number values.
new BigNumber(2e+308)                     // 'Infinity'
new BigNumber(1e-324)                     // '0'

// Precision loss from the unexpected result of arithmetic with Number values.
new BigNumber(0.7 + 0.1)                  // '0.7999999999999999'


npm install --save decimal.js

 const a = 9.99;
 const b = 8.03;
 
 // 加法
 let c = new Decimal(a).add(new Decimal(b)) 
 
 // 减法
 let d = new Decimal(a).sub(new Decimal(b))
 
 // 乘法
 let e = new Decimal(a).mul(new Decimal(b))
 
 // 除法
 let f = new Decimal(a).div(new Decimal(b))
相关推荐
deli00712 分钟前
1 个汉字算几个 token?实测中英文 prompt 与代码的编码差异
前端
无限聚核19 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966228 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人44 分钟前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly2 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战