Big.Js -- 高精度数值计算

2,558 阅读3分钟

官网:mikemcl.github.io/big.js/

安装

# pnpm 
pnpm add big.js

# yarn
yarn add big.js

# npm 
npm install big.js

项目使用 TypeScript版本

# pnpm 
pnpm add big.js
pnpm add @types/big.js -D

# yarn
yarn add big.js
yarn add @types/big.js -D

# npm 
npm install big.js
npm install @types/big.js -D

注意

1、默认传入内容不会类型转换

2、const num = new Big()​ 无法直接使用 需要使用 toString​ 或者 toNumber​ 进行转换才能使用

3、接受的内容 不会直接进行转化 比如说 null​ 还有 undefine​,需要额外进行处理

使用

import Big from 'big.js'

相关方法

初始化Big number数据

const number = new Big(.1)

// 不推荐 因为不明显
const number = Big(.1)

加减法(plus | minus)

// 加
const sum = new Big(0.1).plus(0.2);
console.log(sum.toString()); // "0.3"

// 减
const difference = new Big(0.3).minus(0.2);
console.log(difference.toString()); // "0.1"

乘除法运算(times | div)

// 乘
const product = new Big(0.1).times(3);
console.log(product.toString()); // "0.3"

// 除
const quotient = new Big(0.3).div(3);
console.log(quotient.toString()); // "0.1"

取整(round)

  • 可以进行四舍五入
const num1 = new Big(1.235);
console.log(num1.round(2).toString()); // "1.24" (保留两位小数,四舍五入)
console.log(num1.round(2, 1).toString()); // "1.23" (向下取整)
console.log(num1.round(2, 2).toString()); // "1.24" (向上取整)
console.log(num1.round(2, 3).toString()); // "1.23" (截断)

取整toFixed()

  • 不进行四舍五入
const result = new Big(1.23456).toFixed(2);
console.log(result); // "1.23"

转化成 number(toNumber)

  • 返回 JavaScript 原生数字类型
  • 可能因浮点数限制导致精度损失不适合高精度场景
const num = new Big(0.1).plus(0.2); // 0.1 + 0.2
console.log(num.toNumber()); // 0.3

转化成 string(toString)

  • 完全保留精度
  • 数据展示、存储高精度值
const num = new Big(0.1).plus(0.2); // 0.1 + 0.2
console.log(num.toString()); // "0.3"

取绝对值(abs)

const num1 = new Big(-0.999);

console.log(num1.abs()); // Big2 {s: 1, e: -1, c: Array(3)}
console.log(num1.abs().toNumber()); // '0.999'
console.log(Number(num1.abs())); // '0.999'

相等(eq)

const isEqual = new Big(0.3).eq(0.1 + 0.2); // 注意:0.1 + 0.2 是浮点数计算,有误差
console.log(isEqual); // false

const isEqualFixed = new Big(0.3).eq(new Big(0.1).plus(0.2));
console.log(isEqualFixed); // true

大于或者大于等于

// gt (大于)
console.log(0.1 > 0.3 - 0.2); // true
// 原因:0.3 - 0.2 的结果是 0.09999999999999998,0.1 > 0.09999999999999998

console.log(new Big(0.1).gt(new Big(0.1).minus(0.2))); // false
// 原因:Big.js 精确计算,0.1 > (0.1 - 0.2),即 0.1 > -0.1,结果为 false

---------------------------------------------------------------------------------
// gte (大于等于)
console.log(0.3 - 0.2 <= 0.1); // true
// 原因:0.09999999999999998 <= 0.1,结果为 true

console.log(new Big(0.1).gte(new Big(0.3).minus(0.2))); // true
// 原因:Big.js 精确计算,0.1 >= (0.3 - 0.2),即 0.1 >= 0.1,结果为 true

小于或小于等于

// lt (小于)
console.log(0.1 < 0.3 - 0.2); // false
// 原因:0.1 < 0.09999999999999998,结果为 false

console.log(new Big(0.1).lt(new Big(0.3).minus(0.2))); // true
// 原因:Big.js 精确计算,0.1 < (0.3 - 0.2),即 0.1 < 0.1,结果为 true

---------------------------------------------------------------------------------
// lte (小于等于)
console.log(0.3 - 0.2 <= 0.1); // true
// 原因:0.09999999999999998 <= 0.1,结果为 true

console.log(new Big(0.1).lte(new Big(0.3).minus(0.2))); // true
// 原因:Big.js 精确计算,0.1 <= (0.3 - 0.2),即 0.1 <= 0.1,结果为 true

业务封装使用

/**
 * 格式化数值 乘以100
 * @param value number
 * @returns number * 100
 */
export function formatTimes100(value: number) {
  if (!isNumber(value)) {
    return null;
  }

  return new Big(value).times(100).toNumber();
}

/**
 * 格式化数值 除以100
 * @param value number
 * @returns number / 100
 */
export function formatDiv100(value: number) {
  if (!isNumber(value)) {
    return null;
  }

  return new Big(value).div(100).round(4).toNumber();
}

两数相除

/**
 * @param x  number | null | string
 * @param y  number | null | string
 * @returns x / y bao
 */

function itemDiv(x, y) {
  try {
    return new Big(x || 0)
      .div(new Big(y) || 0)
      .round(2)
      .toNumber();
  } catch (error) {
    return 0;
  }
}

通用计算

import Big from "big.js";

/**
 * 通用合计计算函数
 * @param dataList 数据数组
 * @param field 字段名称,用于提取需要计算的数值
 * @param filterFn 可选的过滤函数,返回 true 的项将被计入合计
 * @param decimalPlaces 小数位保留数,默认保留两位小数
 * @returns 合计结果,保留指定小数位
 */
function calculateTotal(
  dataList: Array<Record<string, any>>,
  field: string,
  filterFn?: (item: Record<string, any>) => boolean,
  decimalPlaces: number = 2
): string {
  const total = dataList.reduce((current, item) => {
    // 如果有过滤函数,先进行过滤判断
    if (filterFn && !filterFn(item)) {
      return current;
    }
    // 累加指定字段的值,默认值为 0
    return current.plus(new Big(item[field] || 0));
  }, new Big(0));

  // 返回结果,保留指定小数位数
  return total.round(decimalPlaces).toString() || "0.00";
}



----------------------------------------------------------------------
// 简单合计
const data = [
  { value: 10 },
  { value: 20 },
  { value: 30 },
];

const total = calculateTotal(data, "value");
console.log(total); // "60.00"

----------------------------------------------------------------------
// 带过滤条件的合计
const data = [
  { selectionStatus: true, value: 10 },
  { selectionStatus: false, value: 20 },
  { selectionStatus: true, value: 30 },
];

const total = calculateTotal(
  data,
  "value",
  (item) => item.selectionStatus // 仅计算 selectionStatus 为 true 的项
);
console.log(total); // "40.00"


----------------------------------------------------------------------
// 自定义小数位
const data = [
  { value: 10.123 },
  { value: 20.456 },
  { value: 30.789 },
];

const total = calculateTotal(data, "value", undefined, 1); // 保留 1 位小数
console.log(total); // "61.4"


----------------------------------------------------------------------
const data = [
  { value: 10 },
  { value: null },
  { value: undefined },
  { value: 20 },
];

const total = calculateTotal(data, "value");
console.log(total); // "30.00"