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

相关推荐
Nan_Shu_6141 天前
学习: Threejs (2)
前端·javascript·学习
G_G#1 天前
纯前端js插件实现同一浏览器控制只允许打开一个标签,处理session变更问题
前端·javascript·浏览器标签页通信·只允许一个标签页
@大迁世界1 天前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路1 天前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug1 天前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu121381 天前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中1 天前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 天前
GDAL 实现矢量合并
前端
hxjhnct1 天前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 天前
javascript逻辑运算符
开发语言·javascript·ecmascript