JavaScript 中的位运算溢出:为什么 `(2**31) | 0` 会变成一个负数?

【技术讲座】JavaScript 中的位运算溢出:(2**31) | 0 为什么会变成负数?

引言

在 JavaScript 中,位运算是一种非常基础且强大的操作,它允许开发者以位级别对数字进行操作。然而,由于 JavaScript 的数值表示方式,位运算有时会导致意想不到的结果,特别是当涉及到整数溢出时。本文将深入探讨 JavaScript 中的位运算溢出问题,特别是为什么 (2**31) | 0 会变成一个负数。

JavaScript 的数值表示

JavaScript 使用 IEEE 754 双精度浮点数格式来表示数值。这意味着 JavaScript 中的所有数字都是浮点数,即使是整数。在 JavaScript 中,数字的表示范围从 -2^53 + 1 到 2^53 – 1。

位运算与溢出

位运算包括按位与(&)、按位或(|)、按位异或(^)、按位取反(~)和左移(<<)、右移(>>)等。当进行位运算时,如果操作数超出了 JavaScript 能表示的范围,就会发生溢出。

(2**31) | 0 为什么会变成负数

在 JavaScript 中,当我们对一个大整数进行位运算时,如果不进行显式类型转换,JavaScript 会自动将其转换为 32 位的无符号整数。这是因为 JavaScript 的数字类型在内部表示为 64 位浮点数,但在进行位运算时,它会被截断为 32 位。

以下是一个简单的例子:

let num = 2 ** 31;
console.log(num); // 输出:2147483648

let unsignedNum = num | 0;
console.log(unsignedNum); // 输出:2147483648

在上面的代码中,num 是一个 32 位的无符号整数,其值为 2147483648。当我们对 num 进行按位或运算 | 0 时,JavaScript 会将 num 转换为 32 位无符号整数,并保留其值。

然而,如果我们尝试将 num 转换为一个负数:

let negativeNum = -(2 ** 31);
console.log(negativeNum); // 输出:-2147483648

这里,negativeNum 是一个负数,其值为 -2147483648。如果我们对 negativeNum 进行相同的位运算:

let unsignedNegativeNum = negativeNum | 0;
console.log(unsignedNegativeNum); // 输出:2147483648

结果却是一个正数。这是因为 JavaScript 在进行位运算时,会将负数转换为无符号整数,然后执行位运算。

代码示例

以下是一些使用不同语言的代码示例,展示了如何处理 JavaScript 中的位运算溢出:

PHP

<?php
$number = 2147483648;
echo $number | 0; // 输出:2147483648
echo ~$number; // 输出:-2147483649
?>

Python

number = 2147483648
print(number | 0) # 输出:2147483648
print(~number) # 输出:-2147483649

Shell

number=2147483648
echo $((number | 0)) # 输出:2147483648
echo $((~number)) # 输出:-2147483649

SQL

-- 假设我们有一个名为 numbers 的表,其中有一个名为 num 的列
SELECT num | 0 AS unsigned_num, ~num AS unsigned_negative_num
FROM numbers;

结论

在 JavaScript 中,位运算溢出是一个常见的问题,特别是在处理大整数时。理解 JavaScript 的数值表示和位运算规则对于避免这种问题至关重要。通过显式类型转换和注意数值范围,我们可以确保位运算的结果符合预期。

发表回复

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