【JavaScript基础系列】BigNumber运算

一、什么是精度丢失

Tips:这个问题有很多的解决方式,通过一些主流的库可以有效避免
在计算数字时,有时会遇到精度丢失的问题;顾名思义,就是计算后得到的结果不是我们想要的结果。

举个例子:

console.log(0.05 + 0.01) // 0.060000000000000005
// 本应该是 0.06,但是实际的结果是 0.060000000000000005

这是因为js小数点后的运算是会出现精度丢失的问题

1.1 原因:(乘2取整法)

js中 number类型运算都需要先将十进制转二进制。但小数点后的位数转二进制会出现无限循环的问题,只能舍0入1,所以会出现小数点丢失问题。

0.1 => 首先将 0.1 乘以 2,得到 0.2。将 0.2 的整数部分 0 写入二进制小数的第一位,然后将 0.2 的小数部分 0.2 - 0 = 0.2 继续乘以 2,得到 0.4。将 0.4 的整数部分 0 写入二进制小数的第二位,然后将 0.4 的小数部分 0.4 - 0 = 0.4 继续乘以 2,得到 0.8。将 0.8 的整数部分 0 写入二进制小数的第三位,然后将 0.8 的小数部分 0.8 - 0 = 0.8 继续乘以 2,得到 1.6。将 1.6 的整数部分 1 写入二进制小数的第四位,然后将 1.6 的小数部分 1.6 - 1 = 0.6 继续乘以 2,得到 1.2。将 1.2 的整数部分 1 写入二进制小数的第五位,然后将 1.2 的小数部分 1.2 - 1 = 0.2 继续乘以 2,得到 0.4。由于小数部分已经出现过,因此小数部分开始循环,直到达到所需的精度。
转换的结果为 0.00011001100110011001100110011001100110011001100110011010…,其中小数部分无限循环。这个值和 0.1 相差比较大,精度已经丢失了。

1.2 科学记数法

将 0.1 转换为科学计数法形式,即 1.0 * 10^-1,然后将 10^-1 转换为二进制小数形式,得到 0.0 0011 0011 0011 0011 0011 0011 0011…。这个值和 0.1 相差较小,但精度仍然丢失。

二、toFixed(适合固定精度)

toFixed() 方法,参数为保留几位小数

  • number.toFixed(digits) ; // digits - 小数点后显示的位数。
  • Return:数字的字符串表示形式,不使用指数表示法,并且具有小数digits后的确切digits 。
// 无修饰
console.log(33.01 - 5) // 28.009999999999998
// 第一版本:toFixed修饰
console.log((33.01 - 5).toFixed(2)) // 28.01
// toFixed精度为3
console.log((33.01 - 5).toFixed(3)) // 28.010
// 借助 parseFloat 移除多余的0
console.log(parseFloat((33.01 - 5).toFixed(3))) // 28.010

三、倍数操作(特定场景)

比如要保留两位小数 那我就乘100,运算完后再除100

// 无修饰
console.log(0.05 + 0.01); // 0.060000000000000005

// 倍数处理修饰
console.log((0.05 * 100 + 0.01 * 100) / 100); // 0.06

注意:运算中,数字乘10的倍数后,必须是整数,没有小数点出现。否则,就会出现第一种情况,同样导致精度丢失。
所以这是特定场景下的解决方案,需要注意

四、mathjs(泛用)更新频率高

Math.js是JavaScript和Node.js的一个广泛的数学库。它具有一个灵活的表达式解析器

4.1 引入

npm install mathjs
or:https://mathjs.org/download.html

4.2 Use

import {
  atan2, chain, derivative, e, evaluate, log, pi, pow, round, sqrt
} from 'mathjs'

// functions and constants
round(e, 3)                    // 2.718
atan2(3, -3) / pi              // 0.75
log(10000, 10)                 // 4
sqrt(-4)                       // 2i
pow([[-1, 2], [3, 1]], 2)      // [[7, 0], [0, 7]]
derivative('x^2 + x', 'x')     // 2 * x + 1

// expressions
evaluate('12 / (2.3 + 0.7)')   // 4
evaluate('12.7 cm to inch')    // 5 inch
evaluate('sin(45 deg) ^ 2')    // 0.5
evaluate('9 / 3 + 2i')         // 3 + 2i
evaluate('det([-1, 2; 3, 1])') // -7

// chaining
chain(3)
  .add(4)
  .multiply(2)
  .done()  // 14

4.3 精度计算

const num = math.format(math.multiply(math.bignumber(0.5), 3)); // 0.45

const num1 = math.add(math.bignumber(0.1), math.bignumber(0.2)); // 0.3

五、big.js

一个小型、快速的JavaScript库,用于任意精度的十进制运算。https://npmmirror.com/package/big.js

5.1 引入

Add Big to global scope:

<script src='path/to/big.js'></script>

ES module:

<script type='module'>
import Big from './path/to/big.mjs';

Get a minified version from a CDN:

<script src='https://cdn.jsdelivr.net/npm/[email protected]/big.min.js'></script>

5.2 install

$ npm install big.js

CommonJS:

const Big = require('big.js');

ES module:

import Big from 'big.js';

5.3 Use

x = new Big(123.4567)
y = Big('123456.7e-3')                 // 'new' is optional
z = new Big(x)
x.eq(y) && x.eq(z) && y.eq(z)          // true
返回新的结果,精度计算
0.3 - 0.1                              // 0.19999999999999998
x = new Big(0.3)
x.minus(0.1)                           // "0.2"
x                                      // "0.3"
链式调用
x.div(y).plus(z).times(9).minus('1.234567801234567e+8').plus(976.54321).div('2598.11772')
x.sqrt().div(y).pow(3).gt(y.mod(z))    // true
支持javascript中原生number的api
x = new Big(255.5)
x.toExponential(5)                     // "2.55500e+2"
x.toFixed(5)                           // "255.50000"
x.toPrecision(5)                       // "255.50"

你可能感兴趣的:(javascript,开发语言,ecmascript)