js数字计算

js数字计算问题

为什么JS计算会有精度问题
复制代码
      const a = 0.1;
      const b = 0.2;
      console.log(a + b); //0.30000000000000004

JavaScript 使用64位双精度浮点数来表示所有数字。由三部分组成:

  1. 符号位 (1 bit) - 表示正负
  2. 指数位 (11 bits) - 表示数量级
  3. 尾数位 (52 bits) - 表示精度

一》二进制无法精确表示某些十进制小数

比如,十进制的0.1和0.2在二进制中是无限循环小数,由于尾数只有52位,必须进行舍入,导致精度丢失

二》浮点数运算经过两次转换

  1. 十进制转二进制(精度丢失)
  2. 二进制计算
  3. 二进制转十进制(精度丢失)

三》存储空间有限

  • 64位浮点数能精确表示的整数范围是 -2^53 + 12^53 - 1 (即 -90071992547409919007199254740991)

  • 超出这个范围的整数也会出现精度问题

    所以计算大数字会出现问题

    复制代码
          const bigNum = 9007199254740992; // 2^53
          console.log(bigNum === bigNum + 1); // true
浮点数的计算精度问题

1.转为整数再计算

复制代码
      const a = 0.1;
      const b = 0.2;
      console.log((a * 100 + b * 100) / 100); // 0.3

小数四舍五入精度问题:

toFixed()

能四舍五入保留指定位数的小数, 0-20 之间的整数

它返回的是字符串,而不是数字

它的取舍原则:

  1. 四舍六入五考虑‌:当被修约的数字小于5时,该数字舍去;大于5时,则进位;等于5时,要看5前面的数字,若是奇数则进位,若是偶数则将5舍掉,即修约后末尾数字都成为偶数;若5的后面还有不为"0"的任何数,则此时无论5的前面是奇数还是偶数,均应进位‌12。

  2. 五后非零就进一‌:如果被修约的数字5后面有非零数字,则进位‌2。

  3. 五后为零看奇偶‌:如果被修约的数字5后面是零,则看5前面的数字,如果是奇数则进位,如果是偶数则舍去‌

    101.595.toFixed(2);// '101.59'

这里看上去是595,但是实际存储值的小数部分约为 101.59500000000002,所以打印出的是101.59

Math.round()

四舍五入取整;函数在内部实现时,会先将传入的数字加上0.5,然后向下取整。如果是四舍五入保留两位那原来数字小数不止两位的话也可能会出现精度问题

复制代码
Math.round(101.595 * 100) / 100 // 101.59

因为JS实际算出来为101.595 * 100=10159.499999996,加上0.5向下取整是10159,再除以100也没得到321201.60

2.利用第三方库

math.js

mathjs功能全面,体积较大

基本加减乘除语法:

复制代码
math.add(a,b)
math.subtract(a,b)
math.multiply(a,b)
math.divide(a,b)

链式调用:

复制代码
let result = math.chain(3)
   .add(4)
   .multiply(2)
   .done();
   console.log(result); // 14

可以计算表达式,也支持大数字的计算:

可利用math.js的BigNumber模式解决精度问题

复制代码
import { create, all } from 'mathjs';
// 配置使用 BigNumber
const math = create(all, {
  number: 'BigNumber',
  precision: 64
});
// 计算示例
console.log(math.evaluate('0.1 + 0.2').toString()); // "0.3"
console.log(math.evaluate('1 / 3').toString());     // "0.33333333333333333333"

decimal.js

适合高精度小数计算

复制代码
plus(): 加法
minus(): 减法
times(): 乘法
div(): 除法
mod(): 取模(余数)
abs(): 绝对值
sqrt(): 平方根
pow(): 幂运算
toFixed(): 四舍五入,控制小数位数
toPrecision(): 设置有效数字

big.js

decimal.js语法:和big.js基本一样

轻量级

复制代码
      const num1 = new Big(0.1);
      const num2 = new Big(0.2); // 传入字符串形式的数字,避免精度丢失
      console.log(num1.plus(num2).toString()); // 0.3

三者对比:

第三方库 math.js decimal.js big.js
定位 全能数学库 高精度十进制计算 轻量级精确计算
大小 约 100KB+ 约 32KB 约6KB
功能 极其广泛 财务/科学计算 基础算术运算
性能 较慢 适中 最快
精度处理 可配置 可配置(默认20位) 固定(默认20位)
数字类型 支持多种类型 仅Decimal 仅Big
大数字的计算精度问题

1.BigInt

ES6中新增的一种基本数据类型,结尾比普通数字多了个n

浮点数不能直接转换为BigInt,会报错

复制代码
      const bigNum = 9007199254740992; // 2^53
      console.log(bigNum); //9007199254740992;
      console.log(BigInt(bigNum)); //9007199254740992n
      console.log(bigNum === bigNum + 1); // true
      console.log(BigInt(bigNum) === BigInt(bigNum) + BigInt(1)); // false

 console.log(typeof BigInt(bigNum)); //bigint

2.使用第三方库

decimal.js、big.js、math.js

相关推荐
宁静_致远2 分钟前
React 性能优化:深入理解 useMemo 、useCallback 和 memo
前端·react.js·面试
旺仔牛仔QQ糖3 分钟前
项目中TypeScript 编译器的工作流程
前端·typescript
coding丨3 分钟前
自制微信小程序popover菜单,气泡悬浮弹窗
前端·javascript·vue.js
anyup11 分钟前
10000+ 个点位轻松展示,使用 Leaflet 实现地图海量标记点聚类
前端·数据可视化·cursor
林太白13 分钟前
Rust认识安装
前端·后端·rust
掘金酱14 分钟前
🔥 稀土掘金 x Trae 夏日寻宝之旅火热进行ing:做任务赢大疆pocket3、Apple watch等丰富大礼
前端·后端·trae
1024小神14 分钟前
tauri项目添加多文件下载功能,并支持下载进度回调显示在前端页面上
前端·javascript
Ace_317508877615 分钟前
义乌购拍立淘API接入指南
前端
不想说话的麋鹿21 分钟前
《NestJS 实战:RBAC 系统管理模块开发 (四)》:用户绑定
前端·后端·全栈
我是谁谁34 分钟前
JavaScript 中的 Map、WeakMap、Set 详解
前端