ITPUB

为什么 num === num - 1 的结果会为true?

在 JavaScript 中, num === num - 1 的等式成立需要满足数值或类型的特殊条件。

典型场景

1. 无穷大(Infinity)

当 num 为 Infinity 时,任何数学运算都不会改变其值,因此:
const num = Infinity;
console.log(num === num - 1);  // true
console.log(num - 1);  //Infinity
因为 Infinity - 1 仍为 Infinity ,类型和值均相同。

2. 超出精度范围的大数

当 num 超过 JavaScript 的整数精确表示范围(超过 Number.MAX_SAFE_INTEGER ,即 2^53 - 1 )时,由于 IEEE 754 双精度浮点数的精度限制,连续的整数可能无法区分,此时 num 和 num-1 在内存中有可能被存储为相同的浮点数。
const num = Number.MAX_SAFE_INTEGER + 3;
console.log(num === num - 1);    //false
console.log(num,num-1);  
//9007199254740994 9007199254740992
const num1 = Number.MAX_SAFE_INTEGER + 4;
console.log(num1 === num1 - 1);   //true 
console.log(num1,num1-1);  
//9007199254740996 9007199254740996
我们看一下 [MAX_SAFE_INTEGER,MAX_SAFE_INTEGER+6] 的值都是什么:
console.log(Number.MAX_SAFE_INTEGER);
console.log(Number.MAX_SAFE_INTEGER+1);
console.log(Number.MAX_SAFE_INTEGER+2);
console.log(Number.MAX_SAFE_INTEGER+3);
console.log(Number.MAX_SAFE_INTEGER+4);
console.log(Number.MAX_SAFE_INTEGER+5);
console.log(Number.MAX_SAFE_INTEGER+6);
从上面结果中我们可以看到 [MAX_SAFE_INTEGER,MAX_SAFE_INTEGER+6] 的值是有点混乱的,所以超过 Number.MAX_SAFE_INTEGER 的数字我们就不能用常规的计算方法了。

怎么规避

验证安全整数范围

通过 Number.isSafeInteger() 可以检测数值是否在安全范围内:
console.log(Number.isSafeInteger(666)); //true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER)); //true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 6)); //false
console.log(Number.isSafeInteger(9007199254740999)); //false

使用 BigInt 处理大整数

JavaScript 从 ES2020 开始支持 BigInt 类型,可直接处理任意精度的整数。对于可能超出安全整数范围的整数,如果需要精确计算,我们可以将数值转换为 BigInt 类型。
// 创建 BigInt(后缀加 n 或调用构造函数)
const a = 123456789012345678901234567890n;
const b = BigInt("987654321098765432109876543210");

// 基本运算
console.log(a + b); // 1111111110111111111011111111100n
console.log(a * b); // 12193263113702179522374638011189951891063930922378900n

// 注意事项:BigInt 不能与普通 Number 混合运算
const c = 10;
console.log(a + c); // 报错,需转换为同类型:a + BigInt(c)
>>> 还有要注意的是 :BigInt 只能进行纯整数运算
那么如果涉及到高精度浮点数计算我们该怎么办呢?

bignumber.js

bignumber.js 是一个比较成熟的高精度计算库,涉及浮点数或更复杂的运算时我们可以使用这个库。
import BigNumber from 'bignumber.js';

const x = new BigNumber("123456789.123456789");
const y = new BigNumber("987654321.987654321");

// 高精度运算(支持小数)
console.log(x.plus(y).toString());  // "1111111111.11111111"
console.log(x.multipliedBy(y).toFixed(2)); // "121932631137021795.22"
>>> 当然,如果你喜欢,我们也可以自己简单实现一个大数运算,这里我们一起来实现一个大数加法试试

手写大数加法

思路

简单来说就是要模拟一个加法竖式的运算过程,所以我们需要思考一下自己在做加法竖式计算的时候是怎么做的?
  • 1、个位数对齐,从后往前依次相加
不管是否带有小数,我们都是将两个数字的个位数对齐来做计算。
  • 2、检查是否需要进位和是否有进位
相加大于等于10的时候,需要进位加1
  • 3、两个字符串都遍历完时即可得出结果

代码实现

function addStrings(num1, num2) {
  num1 = (num1 || "0") + "";
  num2 = (num2 || "0") + "";
let dec1Len = num1.split(".")[1]?.length || 0,
    dec2Len = num2.split(".")[1]?.length || 0;
if (dec1Len || dec2Len) {
    if (!num1.includes(".")) {
      num1 += ".";
    }
    if (!num2.includes(".")) {
      num2 += ".";
    }
    const maxDecLen = Math.max(dec1Len, dec2Len);
    num1 += "0".repeat(maxDecLen - dec1Len);
    num2 += "0".repeat(maxDecLen - dec2Len);
  }
let i = num1.length - 1,
    j = num2.length - 1,
    add = 0,
    ans = "";
//从后往前遍历
while (i >= 0 || j >= 0 || add != 0) {
    if (num1[i] === ".") {
      ans = "." + ans;
      i--;
      j--;
      continue;
    }
    //每一位转换为数字
    const x = i >= 0 ? num1.charAt(i) - "0" : 0;
    const y = j >= 0 ? num2.charAt(j) - "0" : 0;
    //保存该位的计算结果
    const result = x + y + add;
    ans = (result % 10) + ans;
    add = Math.floor(result / 10);
    i -= 1;
    j -= 1;
  }
return ans;
}

测试一下

console.log(
  addStrings(
    "11111111111111111111111111111.11111111111111111111",
    "11111111111111111111111111111.99999999999999999999"
  )
);
//22222222222222222222222222223.11111111111111111110

console.log(
  addStrings(
    "11111111111111111111111111111.11111111111111111111",
    "11111111111111111111111111111"
  )
);
//22222222222222222222222222222.11111111111111111111

console.log(addStrings("9", "2"));
//11




直播预告 6月27 日 ,ITPUB携手行业专家围绕“ AI幻觉是神,还是魔? ” 这一主题开展线上直 播分享,点击下方卡片预约直播: 7月1 日 ,ITPUB携手行业专家围绕“ 深入浅出机器学习从数据到AI算法 ” 这一主题开展线上直 播分享,点击下方卡片预约直播: