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

相关推荐
幼儿园技术家3 分钟前
Diff算法的简单介绍
前端
陈随易4 分钟前
为VSCode扩展开发量身打造的UI库 - vscode-elements
前端·后端·程序员
子壹6 分钟前
大文件分片上传
javascript·node.js
叁金Coder8 分钟前
业务系统跳转Nacos免登录方案实践
前端·javascript·nginx·nacos
蓝倾8 分钟前
京东商品销量数据如何获取?API接口调用操作详解
前端·api·fastapi
stoneship8 分钟前
满帮微前端
前端
GKDf1sh9 分钟前
【前端安全】聊聊 HTML 闭合优先级和浏览器解析顺序
前端·安全·html
前端老鹰10 分钟前
HTML <dialog> 元素:原生弹窗解决方案,无需再写复杂遮罩
前端·html
CodeTransfer16 分钟前
今天给大家带来的是一个简单的小球抛物线动画效果
前端·javascript
宁静_致远25 分钟前
使用 React 实现高效的接口轮询与高实时性通信:性能优化与最佳实践
前端·javascript·面试