JS `BigInt` 在加密货币、高精度计算中的应用

各位朋友,晚上好!今天咱们来聊聊 JavaScript 中的 BigInt,这玩意儿在加密货币和高精度计算领域可是个宝贝。别看名字挺唬人,其实用起来也没那么复杂,我尽量用大白话给大家讲明白。

一、 啥是 BigInt? 为什么要它?

首先,得说说 JavaScript 里的数字。以前,JavaScript 只能精确表示 -2^53 + 12^53 - 1 之间的整数,也就是 Number 类型的 Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER。超过这个范围,精度就没法保证了,会出现一些奇奇怪怪的现象。不信?试试看:

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992  <- 啥?
console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994

你看,加 2 之后居然和加 1 的结果一样了!这是因为 JavaScript 的 Number 类型是用双精度浮点数来表示的,超过安全范围就没法精确表示了。

这在很多场景下是不能接受的。比如:

  • 加密货币: 加密货币的交易金额、账户余额,那都是动辄几十上百位的数字,精度稍微有点偏差,你的比特币可能就凭空消失了,这谁受得了?
  • 高精度计算: 科学计算、金融计算,对精度要求非常高,差一点点结果就谬以千里。
  • 大整数 ID: 一些系统用超长的整数作为 ID,Number 类型也hold不住。

所以,我们需要一种能够精确表示任意大小整数的类型,BigInt 就应运而生了。

二、 BigInt 怎么用?

BigInt 的用法也很简单,有两种方式:

  1. 在数字后面加 n

    const bigNumber = 12345678901234567890n;
    console.log(typeof bigNumber); // "bigint"
  2. 使用 BigInt() 函数:

    const bigNumber2 = BigInt(12345678901234567890);
    const bigNumber3 = BigInt("12345678901234567890"); // 从字符串转换
    console.log(typeof bigNumber2); // "bigint"
    console.log(typeof bigNumber3); // "bigint"

注意,BigInt() 函数可以接受数字或者字符串作为参数,但是不能接受小数。

三、 BigInt 的运算

BigInt 支持大部分常用的算术运算符,比如 +-*/%**

const a = 12345678901234567890n;
const b = 9876543210987654321n;

console.log(a + b); // 13333333333333333311n
console.log(a * b); // 1219326311126352690219326311126352690n
console.log(a / b); // 1n  (注意,结果是整数,会截断小数部分)
console.log(a % b); // 12345678901234567890n

console.log(a ** 2n); // 152415787532388367501905199875019052100n

重要的事情说三遍:

  • BigInt 只能和 BigInt 运算!
  • BigInt 只能和 BigInt 运算!
  • BigInt 只能和 BigInt 运算!

不能直接和 Number 类型的值进行运算,否则会报错。

const a = 10n;
const b = 5;

// console.log(a + b); // TypeError: Cannot mix BigInt and other types, use explicit conversions

console.log(a + BigInt(b)); // 15n  (需要先将 Number 转换成 BigInt)
console.log(Number(a) + b); // 15 (需要将 BigInt 转换成 Number,但可能丢失精度)

注意精度丢失的风险!BigInt 转换成 Number 类型可能会丢失精度,所以要谨慎使用。 最好只在确定结果在 Number 的安全范围内时才进行转换。

四、 BigInt 在加密货币中的应用

加密货币,比如比特币、以太坊,交易金额、账户余额都是很大的整数,而且对精度要求极高。BigInt 刚好可以满足这个需求。

举个例子,假设我们要实现一个简单的比特币交易函数:

// 模拟账户余额
let aliceBalance = 10000000000000000000n; // 10 BTC (假设 1 BTC = 10^18 satoshi)
let bobBalance = 5000000000000000000n; // 5 BTC

function transfer(senderBalance, receiverBalance, amount) {
  if (senderBalance < amount) {
    console.log("余额不足!");
    return [senderBalance, receiverBalance];
  }

  const newSenderBalance = senderBalance - amount;
  const newReceiverBalance = receiverBalance + amount;

  console.log(`交易成功!转账金额: ${amount}`);
  console.log(`Alice 余额: ${newSenderBalance}`);
  console.log(`Bob 余额: ${newReceiverBalance}`);

  return [newSenderBalance, newReceiverBalance];
}

// Alice 转账 2 BTC 给 Bob
[aliceBalance, bobBalance] = transfer(aliceBalance, bobBalance, 2000000000000000000n); // 2 BTC

// Alice 转账 10 BTC 给 Bob
[aliceBalance, bobBalance] = transfer(aliceBalance, bobBalance, 10000000000000000000n); // 10 BTC

// Alice 转账 100 BTC 给 Bob
[aliceBalance, bobBalance] = transfer(aliceBalance, bobBalance, 100000000000000000000n); // 余额不足

在这个例子中,我们用 BigInt 来表示账户余额和交易金额,确保了计算的精度,避免了因精度问题导致的资金损失。

五、 BigInt 在高精度计算中的应用

除了加密货币,BigInt 在高精度计算领域也有广泛的应用。比如,计算阶乘、计算圆周率等等。

我们来写一个计算阶乘的函数:

function factorial(n) {
  if (n === 0n) {
    return 1n;
  } else {
    return n * factorial(n - 1n);
  }
}

console.log(factorial(20n)); // 2432902008176640000n
console.log(factorial(50n)); // 30414093201713378043612608166064768844377641568960512000000000000n

如果用 Number 类型来计算 factorial(50),早就溢出了,结果会变成 Infinity

六、 BigInt 的一些坑

虽然 BigInt 很好用,但也有一些需要注意的地方:

  1. 性能问题: BigInt 的运算速度比 Number 类型慢,因为它需要更多的内存和计算资源。所以在对性能要求很高的场景下,要慎重使用。

  2. 类型转换: 前面已经说过了,BigInt 只能和 BigInt 运算,需要进行类型转换。而且,将 BigInt 转换成 Number 类型可能会丢失精度。

  3. 不支持某些操作符: BigInt 不支持一元加号操作符 +,因为会被认为是要将值转换为 Number 类型。

    const a = 10n;
    // console.log(+a); // TypeError: Cannot convert a BigInt value to a number
  4. 位运算符: 位运算符(如 |&^>><<)在 BigInt 中使用时,会移除符号位。这在某些场景下可能会导致意想不到的结果。

七、 BigInt 的浏览器兼容性

BigInt 的浏览器兼容性还不是很好,需要注意。

浏览器 支持版本
Chrome 67+
Firefox 68+
Safari 12+
Edge 79+
Node.js 10.4+

如果要在老版本的浏览器中使用 BigInt,可以使用一些 polyfill 库,比如 big-integer

八、 BigInt 的一些实用技巧

  1. 格式化 BigInt: 可以使用 toLocaleString() 方法来格式化 BigInt,使其更易读。

    const bigNumber = 12345678901234567890n;
    console.log(bigNumber.toLocaleString()); // "12,345,678,901,234,567,890"
  2. 比较 BigInt: 可以使用 ><>=<====!== 等比较运算符来比较 BigInt 的大小。

    const a = 10n;
    const b = 20n;
    
    console.log(a > b); // false
    console.log(a < b); // true
    console.log(a === 10n); // true
  3. 使用第三方库: 如果需要更高级的 BigInt 功能,可以使用一些第三方库,比如 jsbibig.js 等。这些库提供了更多的 API 和更优的性能。

九、 总结

BigInt 是 JavaScript 中一个非常重要的特性,它解决了 Number 类型无法精确表示大整数的问题,为加密货币、高精度计算等领域提供了强大的支持。

虽然 BigInt 有一些需要注意的地方,但只要掌握了它的基本用法和注意事项,就能在实际开发中灵活运用,解决各种大整数计算问题。

希望今天的讲座对大家有所帮助! 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注