MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

JavaScript算术操作符的边界条件

2022-12-024.5k 阅读

一、JavaScript 算术操作符概述

JavaScript 作为一种广泛应用于前端和后端开发的编程语言,提供了丰富的算术操作符来进行数值运算。常见的算术操作符包括加法(+)、减法(-)、乘法(*)、除法(/)、取模(%)以及自增(++)和自减(--)操作符等。在常规的数值运算中,这些操作符能满足大多数场景需求,但当涉及到一些特殊值或者边界条件时,就需要深入了解其具体行为。

二、与 NaN 相关的边界条件

  1. NaN 的定义与特性 NaN 即“Not a Number”,它代表一个非数字值。在 JavaScript 中,NaN 具有独特的特性,它与任何值(包括自身)进行比较都不相等。例如:
    console.log(NaN === NaN); // false
    
  2. 算术操作符与 NaN
    • 加法操作符(+:当加法操作的任何一个操作数为 NaN 时,结果都为 NaN
    console.log(5 + NaN); // NaN
    console.log(NaN + NaN); // NaN
    
    • 减法操作符(-:类似加法,只要有一个操作数是 NaN,结果就是 NaN
    console.log(10 - NaN); // NaN
    console.log(NaN - 20); // NaN
    
    • 乘法操作符(*:同样,包含 NaN 的乘法运算结果为 NaN
    console.log(3 * NaN); // NaN
    console.log(NaN * NaN); // NaN
    
    • 除法操作符(/:只要除数或被除数中有 NaN,结果即为 NaN
    console.log(4 / NaN); // NaN
    console.log(NaN / 5); // NaN
    
    • 取模操作符(%:若操作数中有 NaN,结果也是 NaN
    console.log(7 % NaN); // NaN
    console.log(NaN % 8); // NaN
    

三、与无穷大(Infinity)相关的边界条件

  1. Infinity 的概念 在 JavaScript 中,Infinity 表示正无穷大,-Infinity 表示负无穷大。当数值运算结果超出了 JavaScript 所能表示的最大数值范围时,就会得到 Infinity-Infinity。例如,用一个非常大的数除以一个非常小的正数:
    console.log(1e1000 / 1e - 1000); // Infinity
    
  2. 算术操作符与 Infinity
    • 加法操作符(+
      • Infinity 与一个有限数相加时,结果为 Infinity
      console.log(Infinity + 100); // Infinity
      
      • -Infinity 与一个有限数相加时,结果为 -Infinity
      console.log(-Infinity + 200); // -Infinity
      
      • InfinityInfinity 相加时,结果为 Infinity
      console.log(Infinity + Infinity); // Infinity
      
      • -Infinity-Infinity 相加时,结果为 -Infinity
      console.log(-Infinity + -Infinity); // -Infinity
      
      • Infinity-Infinity 相加时,结果为 NaN
      console.log(Infinity + -Infinity); // NaN
      
    • 减法操作符(-
      • 当从 Infinity 减去一个有限数时,结果为 Infinity
      console.log(Infinity - 50); // Infinity
      
      • 当从 -Infinity 减去一个有限数时,结果为 -Infinity
      console.log(-Infinity - 30); // -Infinity
      
      • Infinity 减去 Infinity 时,结果为 NaN
      console.log(Infinity - Infinity); // NaN
      
      • -Infinity 减去 -Infinity 时,结果为 NaN
      console.log(-Infinity - -Infinity); // NaN
      
      • Infinity 减去 -Infinity 时,结果为 Infinity
      console.log(Infinity - -Infinity); // Infinity
      
      • -Infinity 减去 Infinity 时,结果为 -Infinity
      console.log(-Infinity - Infinity); // -Infinity
      
    • 乘法操作符(*
      • Infinity 与一个正数相乘时,结果为 Infinity
      console.log(Infinity * 5); // Infinity
      
      • Infinity 与一个负数相乘时,结果为 -Infinity
      console.log(Infinity * -3); // -Infinity
      
      • -Infinity 与一个正数相乘时,结果为 -Infinity
      console.log(-Infinity * 2); // -Infinity
      
      • -Infinity 与一个负数相乘时,结果为 Infinity
      console.log(-Infinity * -4); // Infinity
      
      • InfinityInfinity 相乘时,结果为 Infinity
      console.log(Infinity * Infinity); // Infinity
      
      • -Infinity-Infinity 相乘时,结果为 Infinity
      console.log(-Infinity * -Infinity); // Infinity
      
      • Infinity-Infinity 相乘时,结果为 -Infinity
      console.log(Infinity * -Infinity); // -Infinity
      
    • 除法操作符(/
      • 当一个有限正数除以 0 时,结果为 Infinity
      console.log(5 / 0); // Infinity
      
      • 当一个有限负数除以 0 时,结果为 -Infinity
      console.log(-3 / 0); // -Infinity
      
      • Infinity 除以一个正数时,结果为 Infinity
      console.log(Infinity / 2); // Infinity
      
      • Infinity 除以一个负数时,结果为 -Infinity
      console.log(Infinity / -3); // -Infinity
      
      • -Infinity 除以一个正数时,结果为 -Infinity
      console.log(-Infinity / 4); // -Infinity
      
      • -Infinity 除以一个负数时,结果为 Infinity
      console.log(-Infinity / -5); // Infinity
      
      • Infinity 除以 Infinity 时,结果为 NaN
      console.log(Infinity / Infinity); // NaN
      
      • -Infinity 除以 -Infinity 时,结果为 NaN
      console.log(-Infinity / -Infinity); // NaN
      
      • Infinity 除以 -Infinity 时,结果为 NaN
      console.log(Infinity / -Infinity); // NaN
      
      • -Infinity 除以 Infinity 时,结果为 NaN
      console.log(-Infinity / Infinity); // NaN
      
    • 取模操作符(%
      • Infinity 对一个有限数取模时,结果为 NaN
      console.log(Infinity % 5); // NaN
      
      • -Infinity 对一个有限数取模时,结果为 NaN
      console.log(-Infinity % 3); // NaN
      

四、整数边界条件

  1. JavaScript 的数字存储 JavaScript 使用 IEEE 754 双精度 64 位格式来存储数字。这意味着 JavaScript 能精确表示的整数范围是有限的。安全整数范围是 -2^53 + 12^53 - 1(即 -90071992547409919007199254740991)。在这个范围之外,整数可能无法精确表示。
  2. 算术操作符与整数边界
    • 加法操作符(+:当两个整数相加的结果超出安全整数范围时,可能会出现精度问题。
    let num1 = 9007199254740990;
    let num2 = 2;
    console.log(num1 + num2); // 9007199254740992,在安全范围内,结果正确
    let largeNum1 = 9007199254740991;
    let largeNum2 = 2;
    console.log(largeNum1 + largeNum2); // 9007199254740992,超出安全范围,结果不准确
    
    • 减法操作符(-:类似加法,减法操作结果超出安全整数范围也会有精度问题。
    let num3 = 9007199254740992;
    let num4 = 9007199254740990;
    console.log(num3 - num4); // 2,在安全范围内,结果正确
    let largeNum3 = 9007199254740993;
    let largeNum4 = 9007199254740991;
    console.log(largeNum3 - largeNum4); // 2,超出安全范围,结果不准确
    
    • 乘法操作符(*:乘法操作如果结果超出安全整数范围,同样会出现精度问题。
    let num5 = 1000000000000000;
    let num6 = 1000000000000000;
    console.log(num5 * num6); // 1e30,超出安全范围,结果不准确
    
    • 除法操作符(/:除法操作如果涉及到超出安全整数范围的数,也可能因为精度问题导致结果不准确。
    let num7 = 1e30;
    let num8 = 1e15;
    console.log(num7 / num8); // 1e15,超出安全范围,结果可能不准确
    
    • 取模操作符(%:当操作数超出安全整数范围时,取模操作也可能受到精度影响。
    let num9 = 9007199254740992;
    let num10 = 3;
    console.log(num9 % num10); // 2,在安全范围内,结果正确
    let largeNum5 = 9007199254740993;
    let largeNum6 = 3;
    console.log(largeNum5 % largeNum6); // 2,超出安全范围,结果可能不准确
    

五、非数值类型与算术操作符

  1. 字符串与算术操作符
    • 加法操作符(+:在 JavaScript 中,当加法操作符的其中一个操作数是字符串时,会进行字符串拼接。
    console.log('5' + 3); // '53'
    console.log(2 + '7'); // '27'
    console.log('Hello' + 'World'); // 'HelloWorld'
    
    • 其他算术操作符(-*/%):当操作数中有字符串且不能隐式转换为有效数字时,结果为 NaN
    console.log('5' - 3); // 2,字符串'5'隐式转换为数字5
    console.log('abc' - 2); // NaN,'abc'不能隐式转换为有效数字
    console.log('7' * 3); // 21,字符串'7'隐式转换为数字7
    console.log('xyz' / 5); // NaN,'xyz'不能隐式转换为有效数字
    console.log('8' % 3); // 2,字符串'8'隐式转换为数字8
    
  2. 布尔值与算术操作符
    • 布尔值 true 在算术运算中会隐式转换为 1false 会隐式转换为 0
    console.log(true + 5); // 6
    console.log(false - 3); // -3
    console.log(true * 4); // 4
    console.log(false / 2); // 0
    console.log(true % 2); // 1
    
  3. null 与算术操作符
    • null 在算术运算中会隐式转换为 0
    console.log(null + 10); // 10
    console.log(null - 5); // -5
    console.log(null * 3); // 0
    console.log(null / 2); // 0
    console.log(null % 4); // 0
    
  4. undefined 与算术操作符
    • undefined 在算术运算中会导致结果为 NaN
    console.log(undefined + 7); // NaN
    console.log(15 - undefined); // NaN
    console.log(undefined * 4); // NaN
    console.log(8 / undefined); // NaN
    console.log(undefined % 3); // NaN
    

六、自增(++)和自减(--)操作符的边界条件

  1. 前置自增/自减
    • 前置自增(++var)和前置自减(--var)操作符会先将变量的值增加或减少 1,然后返回改变后的值。
    let num = 5;
    let result1 = ++num;
    console.log(num); // 6
    console.log(result1); // 6
    let num2 = 10;
    let result2 = --num2;
    console.log(num2); // 9
    console.log(result2); // 9
    
    • 当涉及到边界值时,例如从 Number.MAX_SAFE_INTEGER 前置自增,会导致精度问题。
    let maxSafeInt = Number.MAX_SAFE_INTEGER;
    let newMaxSafeInt = ++maxSafeInt;
    console.log(newMaxSafeInt); // 结果不准确,超出安全整数范围
    
  2. 后置自增/自减
    • 后置自增(var++)和后置自减(var--)操作符会先返回变量的原始值,然后再将变量的值增加或减少 1
    let num3 = 7;
    let result3 = num3++;
    console.log(num3); // 8
    console.log(result3); // 7
    let num4 = 12;
    let result4 = num4--;
    console.log(num4); // 11
    console.log(result4); // 12
    
    • 同样,在边界值情况下,如从 Number.MIN_SAFE_INTEGER 后置自减,会出现精度问题。
    let minSafeInt = Number.MIN_SAFE_INTEGER;
    let newMinSafeInt = minSafeInt--;
    console.log(newMinSafeInt); // 结果不准确,超出安全整数范围
    

七、位操作符的边界条件

  1. 位操作符概述 JavaScript 提供了按位与(&)、按位或(|)、按位异或(^)、按位非(~)、左移(<<)、有符号右移(>>)和无符号右移(>>>)等位操作符。这些操作符在处理整数的二进制表示时非常有用,但也有其特定的边界条件。
  2. 位操作符与边界值
    • 按位与(&:按位与操作符将两个操作数的每一位进行与运算。当操作数超出安全整数范围时,由于 JavaScript 使用 32 位有符号整数进行位运算,会导致精度问题。
    let num5 = 9007199254740991;
    let num6 = 9007199254740992;
    let result5 = num5 & num6;
    console.log(result5); // 结果不准确,超出安全整数范围,按 32 位有符号整数运算
    
    • 按位或(|:按位或操作符将两个操作数的每一位进行或运算,同样在操作数超出安全整数范围时会有精度问题。
    let num7 = 9007199254740993;
    let num8 = 9007199254740994;
    let result6 = num7 | num8;
    console.log(result6); // 结果不准确,超出安全整数范围,按 32 位有符号整数运算
    
    • 按位异或(^:按位异或操作符将两个操作数的每一位进行异或运算,在处理超出安全整数范围的操作数时也会出现精度问题。
    let num9 = 9007199254740995;
    let num10 = 9007199254740996;
    let result7 = num9 ^ num10;
    console.log(result7); // 结果不准确,超出安全整数范围,按 32 位有符号整数运算
    
    • 按位非(~:按位非操作符对操作数的每一位取反。由于 JavaScript 使用 32 位有符号整数进行运算,对于超出安全整数范围的数会有精度问题。
    let num11 = 9007199254740997;
    let result8 = ~num11;
    console.log(result8); // 结果不准确,超出安全整数范围,按 32 位有符号整数运算
    
    • 左移(<<:左移操作符将操作数的二进制表示向左移动指定的位数。如果移动的位数过大,会导致结果超出安全整数范围,出现精度问题。
    let num12 = 10;
    let result9 = num12 << 32;
    console.log(result9); // 结果不准确,移动位数过大,按 32 位有符号整数运算
    
    • 有符号右移(>>:有符号右移操作符将操作数的二进制表示向右移动指定的位数,保持符号位不变。当操作数超出安全整数范围时,会出现精度问题。
    let num13 = -10;
    let result10 = num13 >> 5;
    console.log(result10); // 结果不准确,操作数超出安全整数范围,按 32 位有符号整数运算
    
    • 无符号右移(>>>:无符号右移操作符将操作数的二进制表示向右移动指定的位数,并且在左侧用 0 填充。同样,当操作数超出安全整数范围时,会出现精度问题。
    let num14 = -5;
    let result11 = num14 >>> 3;
    console.log(result11); // 结果不准确,操作数超出安全整数范围,按 32 位有符号整数运算
    

八、特殊情况与兼容性

  1. 不同环境下的差异 虽然 JavaScript 的规范对算术操作符的行为有明确规定,但在不同的 JavaScript 引擎(如 V8、SpiderMonkey 等)以及不同的运行环境(浏览器、Node.js 等)下,可能会存在一些细微的差异。例如,在处理极大型或极小型数字时,某些旧版本的浏览器可能会有不同的表现。
  2. 兼容性处理 为了确保代码在不同环境下的一致性,可以采用一些兼容性处理方法。例如,在进行涉及安全整数范围的运算时,可以使用 BigInt 类型(在支持的环境中)来确保精度。
    if (typeof BigInt === 'function') {
      let bigNum1 = BigInt(9007199254740991);
      let bigNum2 = BigInt(2);
      let sum = bigNum1 + bigNum2;
      console.log(sum); // 正确处理超出安全整数范围的加法
    }
    

通过深入了解 JavaScript 算术操作符的这些边界条件,开发者可以编写更加健壮和可靠的代码,避免在数值运算中出现意外的结果。无论是处理金融计算、科学计算还是日常的前端交互逻辑,对算术操作符边界条件的掌握都是至关重要的。