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

相关推荐
LuciferHuang1 小时前
震惊!三万star开源项目竟有致命Bug?
前端·javascript·debug
GISer_Jing1 小时前
前端实习总结——案例与大纲
前端·javascript
天天进步20151 小时前
前端工程化:Webpack从入门到精通
前端·webpack·node.js
姑苏洛言2 小时前
编写产品需求文档:黄历日历小程序
前端·javascript·后端
知识分享小能手2 小时前
Vue3 学习教程,从入门到精通,使用 VSCode 开发 Vue3 的详细指南(3)
前端·javascript·vue.js·学习·前端框架·vue·vue3
姑苏洛言3 小时前
搭建一款结合传统黄历功能的日历小程序
前端·javascript·后端
hackchen3 小时前
Go与JS无缝协作:Goja引擎实战之错误处理最佳实践
开发语言·javascript·golang
你的人类朋友4 小时前
🤔什么时候用BFF架构?
前端·javascript·后端
知识分享小能手4 小时前
Bootstrap 5学习教程,从入门到精通,Bootstrap 5 表单验证语法知识点及案例代码(34)
前端·javascript·学习·typescript·bootstrap·html·css3