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

相关推荐
qq_5470261791 小时前
Flowable 工作流引擎
java·服务器·前端
刘逸潇20052 小时前
CSS基础语法
前端·css
吃饺子不吃馅3 小时前
[开源] 从零到一打造在线 PPT 编辑器:React + Zustand + Zundo
前端·svg·图形学
小马哥编程4 小时前
【软考架构】案例分析-Web应用设计(应用服务器概念)
前端·架构
鱼与宇4 小时前
苍穹外卖-VUE
前端·javascript·vue.js
啃火龙果的兔子4 小时前
前端直接渲染Markdown
前端
z-robot4 小时前
Nginx 配置代理
前端
用户47949283569154 小时前
Safari 中文输入法的诡异 Bug:为什么输入 @ 会变成 @@? ## 开头 做 @ 提及功能的时候,测试同学用 Safari 测出了个奇怪的问题
前端·javascript·浏览器
没有故事、有酒4 小时前
Ajax介绍
前端·ajax·okhttp
朝新_4 小时前
【SpringMVC】详解用户登录前后端交互流程:AJAX 异步通信与 Session 机制实战
前端·笔记·spring·ajax·交互·javaee