JS `BigInt` (ES2020):处理任意精度整数

各位靓仔靓女,老少爷们,大家好!今天咱来聊聊JavaScript里的“大块头”—— BigInt。这玩意儿啊,说白了,就是用来解决JavaScript处理整数精度问题的一把利器。

开场白:为啥我们需要 BigInt

话说当年,JavaScript它老人家出生的时候,也没想到自己能火成这样。那时候,它定义数字就用一个 Number 类型,基于 IEEE 754 双精度浮点数标准。这标准吧,好处是能表示小数,坏处是整数的精度有限。它能精确表示的整数范围是 -253 到 253 – 1,也就是大约 -9千万亿到 9千万亿之间。

超过这个范围咋办?凉拌呗!JavaScript会默默地帮你进行近似,结果就变成了“不是你想要的结果”。比如说:

console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992  (WTF? 期望是9007199254740993啊!)

看见没?加2的结果跟加1一样!这要是用在金融计算、密码学、或者其他需要精确整数的场景,那可就翻车了。所以,BigInt 就应运而生了,专门来拯救这些“大”数字。

BigInt:闪亮登场!

BigInt 是 ES2020 引入的新类型,它允许我们安全地存储和操作任意大小的整数。记住,是任意大小!只要你内存够大,多大的数都能hold住。

创建 BigInt 的两种姿势

  1. 数字后面加 n: 这是最简单粗暴的方式。

    const bigNumber = 12345678901234567890n;
    console.log(bigNumber); // 12345678901234567890n
    console.log(typeof bigNumber); // bigint
  2. 使用 BigInt() 构造函数: 这种方式更灵活,可以把字符串或者数字转换成 BigInt

    const bigNumberFromString = BigInt("98765432109876543210");
    const bigNumberFromNumber = BigInt(123); //小心使用,超过安全范围的number转换可能不精确
    console.log(bigNumberFromString); // 98765432109876543210n
    console.log(bigNumberFromNumber); // 123n

    注意: 使用 BigInt() 构造函数从 Number 创建 BigInt 时要小心。如果 Number 超出了安全整数范围,转换的结果可能是不精确的。最好是从字符串创建。

BigInt 的运算:小心有坑!

BigInt 支持大部分的算术运算符,比如 +-*/%**。 但是,要注意以下几点:

  1. BigInt 只能和 BigInt 运算: 不能直接和普通的 Number 类型混着算。如果你非要混着算,JavaScript会毫不留情地抛出一个 TypeError

    const bigNumber = 10n;
    const regularNumber = 5;
    
    //console.log(bigNumber + regularNumber); // TypeError: Cannot mix BigInt and other types
    
    // 必须先转换成同一种类型才能运算
    console.log(bigNumber + BigInt(regularNumber)); // 15n
    console.log(Number(bigNumber) + regularNumber); // 15
  2. 除法会向下取整: BigInt 的除法结果会舍弃小数部分,只保留整数部分。

    const a = 10n;
    const b = 3n;
    console.log(a / b); // 3n  (不是 3.3333...)
  3. 位运算符: BigInt 支持位运算符,如 & (按位与), | (按位或), ^ (按位异或), ~ (按位非), << (左移), >> (右移)。

    const x = 5n; // 二进制 101
    const y = 3n; // 二进制 011
    
    console.log(x & y); // 1n (二进制 001)
    console.log(x | y); // 7n (二进制 111)
    console.log(x ^ y); // 6n (二进制 110)
    console.log(x << 1n); // 10n (二进制 1010)
    console.log(x >> 1n); // 2n (二进制 010)
  4. 比较运算符: BigInt 可以使用比较运算符,如 ==, ===, !=, !==, <, >, <=, >=BigIntNumber 在进行松散相等 (==) 比较时,会进行类型转换。 但是,强烈建议使用严格相等 (===) 避免意外的类型转换。

    console.log(10n == 10); // true
    console.log(10n === 10); // false
    console.log(10n > 5);  // true
    console.log(5n > 10); // false
  5. 一元取正/取反运算符: BigInt 支持一元取正 (+) 和取反 (-) 运算符。

    const a = 5n;
    console.log(+a); // 5n
    console.log(-a); // -5n

BigInt 的一些常用方法

BigInt 本身没有太多内置方法,但可以结合其他JavaScript特性进行操作。

  1. 转换为字符串: 使用 String() 或者 toString() 方法将 BigInt 转换为字符串。

    const bigNumber = 12345678901234567890n;
    const str = String(bigNumber);
    console.log(str); // "12345678901234567890"
  2. 转换为 Number (要小心): 可以使用 Number()BigInt 转换为 Number但是! 如果 BigInt 的值超出了 Number 的安全整数范围,转换的结果会失去精度。所以,除非你确定 BigInt 的值在安全范围内,否则不要轻易转换。

    const safeBigNumber = 100n;
    const unsafeBigNumber = 9007199254740992n; // 超过安全范围
    
    console.log(Number(safeBigNumber)); // 100
    console.log(Number(unsafeBigNumber)); // 9007199254740992 (看似一样,但可能不精确)
    console.log(Number(unsafeBigNumber + 1n)); // 9007199254740992 (精度丢失了!)

BigInt 的应用场景

  1. 金融计算: 处理货币、股票等需要高精度计算的场景。

  2. 密码学: 在加密算法中,经常需要处理大整数。

  3. 科学计算: 某些科学计算需要处理超出 Number 安全范围的整数。

  4. ID生成: 生成全局唯一ID,避免ID冲突。

  5. 大型游戏: 一些大型游戏,比如区块链游戏,需要处理大量的数字。

BigInt 的一些限制

  1. 不能用于 Math 对象的方法: Math.sqrt(), Math.pow() 等方法不支持 BigInt。 你需要自己实现这些方法或者使用第三方库。

  2. JSON 序列化/反序列化: 默认情况下,JSON.stringify() 不支持 BigInt。 你需要自己实现序列化和反序列化的逻辑。 一个常见的做法是将 BigInt 转换为字符串。

    const bigNumber = 12345678901234567890n;
    
    const json = JSON.stringify({ value: bigNumber.toString() }); // 转换为字符串
    console.log(json); // {"value":"12345678901234567890"}
    
    const parsed = JSON.parse(json, (key, value) => {
      if (key === 'value') {
        return BigInt(value); // 转换回 BigInt
      }
      return value;
    });
    
    console.log(parsed.value); // 12345678901234567890n
  3. 浏览器兼容性: 虽然BigInt是ES2020标准,但仍然需要考虑浏览器的兼容性。 对于不支持BigInt的浏览器,你需要使用polyfill或者使用其他替代方案。

代码示例:斐波那契数列

BigInt 计算斐波那契数列,妈妈再也不用担心我溢出了!

function fibonacci(n) {
  if (n <= 1) {
    return BigInt(n);
  }

  let a = 0n;
  let b = 1n;

  for (let i = 2; i <= n; i++) {
    const temp = a + b;
    a = b;
    b = temp;
  }

  return b;
}

console.log(fibonacci(10));   // 55n
console.log(fibonacci(50));   // 12586269025n
console.log(fibonacci(100));  // 354224848179261915075n

总结:BigInt 的优缺点

特性 优点 缺点
精度 可以精确表示任意大小的整数,避免了 Number 类型的精度问题。 性能比 Number 慢,因为需要更多的内存和计算资源。
运算 支持大部分的算术运算符和位运算符。 不能直接和 Number 类型混合运算,需要进行类型转换。 除法会向下取整。 不能用于 Math 对象的方法。
应用场景 金融计算、密码学、科学计算、ID生成、大型游戏等需要高精度整数的场景。 JSON 序列化/反序列化需要特殊处理。 浏览器兼容性需要考虑。
使用方式 简单易用,可以通过在数字后面加 n 或者使用 BigInt() 构造函数创建。 Number 创建 BigInt 时要小心,如果 Number 超出了安全整数范围,转换的结果可能是不精确的。

彩蛋:BigIntNumber 的爱恨情仇

BigIntNumber 就像一对欢喜冤家。 Number 擅长处理小数和中小型的整数,速度快,效率高。 BigInt 擅长处理超大整数,精度高,但速度稍慢。 它们各有优缺点,在不同的场景下发挥着不同的作用。

在选择使用 BigInt 还是 Number 时,要根据实际需求进行权衡。 如果你需要处理的整数超出了 Number 的安全范围,或者需要高精度的计算,那么 BigInt 是你的不二之选。 如果你只需要处理中小型的整数,并且对性能有较高的要求,那么 Number 仍然是更好的选择。

总结一下:

BigInt 虽好,但也要注意使用场景和限制。 只有在真正需要处理大整数的时候,才应该考虑使用它。 不要为了用而用,否则可能会适得其反。

好了,今天的 BigInt 讲座就到这里。 希望大家以后在遇到需要处理大整数的场景时,能够想起我今天讲的内容,并且能够熟练地运用 BigInt,写出更加健壮和可靠的代码。

散会! 祝大家编码愉快!

发表回复

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