JavaScript算术操作符的边界条件
2022-12-024.5k 阅读
一、JavaScript 算术操作符概述
JavaScript 作为一种广泛应用于前端和后端开发的编程语言,提供了丰富的算术操作符来进行数值运算。常见的算术操作符包括加法(+)、减法(-)、乘法(*)、除法(/)、取模(%)以及自增(++)和自减(--)操作符等。在常规的数值运算中,这些操作符能满足大多数场景需求,但当涉及到一些特殊值或者边界条件时,就需要深入了解其具体行为。
二、与 NaN
相关的边界条件
NaN
的定义与特性NaN
即“Not a Number”,它代表一个非数字值。在 JavaScript 中,NaN
具有独特的特性,它与任何值(包括自身)进行比较都不相等。例如:console.log(NaN === NaN); // false
- 算术操作符与
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
)相关的边界条件
Infinity
的概念 在 JavaScript 中,Infinity
表示正无穷大,-Infinity
表示负无穷大。当数值运算结果超出了 JavaScript 所能表示的最大数值范围时,就会得到Infinity
或-Infinity
。例如,用一个非常大的数除以一个非常小的正数:console.log(1e1000 / 1e - 1000); // Infinity
- 算术操作符与
Infinity
- 加法操作符(
+
):- 当
Infinity
与一个有限数相加时,结果为Infinity
。
console.log(Infinity + 100); // Infinity
- 当
-Infinity
与一个有限数相加时,结果为-Infinity
。
console.log(-Infinity + 200); // -Infinity
- 当
Infinity
与Infinity
相加时,结果为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
- 当
Infinity
与Infinity
相乘时,结果为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
- 当
- 加法操作符(
四、整数边界条件
- JavaScript 的数字存储
JavaScript 使用 IEEE 754 双精度 64 位格式来存储数字。这意味着 JavaScript 能精确表示的整数范围是有限的。安全整数范围是
-2^53 + 1
到2^53 - 1
(即-9007199254740991
到9007199254740991
)。在这个范围之外,整数可能无法精确表示。 - 算术操作符与整数边界
- 加法操作符(
+
):当两个整数相加的结果超出安全整数范围时,可能会出现精度问题。
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,超出安全范围,结果可能不准确
- 加法操作符(
五、非数值类型与算术操作符
- 字符串与算术操作符
- 加法操作符(
+
):在 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
- 加法操作符(
- 布尔值与算术操作符
- 布尔值
true
在算术运算中会隐式转换为1
,false
会隐式转换为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
- 布尔值
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
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
六、自增(++
)和自减(--
)操作符的边界条件
- 前置自增/自减
- 前置自增(
++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); // 结果不准确,超出安全整数范围
- 前置自增(
- 后置自增/自减
- 后置自增(
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); // 结果不准确,超出安全整数范围
- 后置自增(
七、位操作符的边界条件
- 位操作符概述
JavaScript 提供了按位与(
&
)、按位或(|
)、按位异或(^
)、按位非(~
)、左移(<<
)、有符号右移(>>
)和无符号右移(>>>
)等位操作符。这些操作符在处理整数的二进制表示时非常有用,但也有其特定的边界条件。 - 位操作符与边界值
- 按位与(
&
):按位与操作符将两个操作数的每一位进行与运算。当操作数超出安全整数范围时,由于 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 位有符号整数运算
- 按位与(
八、特殊情况与兼容性
- 不同环境下的差异 虽然 JavaScript 的规范对算术操作符的行为有明确规定,但在不同的 JavaScript 引擎(如 V8、SpiderMonkey 等)以及不同的运行环境(浏览器、Node.js 等)下,可能会存在一些细微的差异。例如,在处理极大型或极小型数字时,某些旧版本的浏览器可能会有不同的表现。
- 兼容性处理
为了确保代码在不同环境下的一致性,可以采用一些兼容性处理方法。例如,在进行涉及安全整数范围的运算时,可以使用
BigInt
类型(在支持的环境中)来确保精度。if (typeof BigInt === 'function') { let bigNum1 = BigInt(9007199254740991); let bigNum2 = BigInt(2); let sum = bigNum1 + bigNum2; console.log(sum); // 正确处理超出安全整数范围的加法 }
通过深入了解 JavaScript 算术操作符的这些边界条件,开发者可以编写更加健壮和可靠的代码,避免在数值运算中出现意外的结果。无论是处理金融计算、科学计算还是日常的前端交互逻辑,对算术操作符边界条件的掌握都是至关重要的。