25

我最近发现了这段 JavaScript 代码:

Math.random() * 0x1000000 << 0

我知道第一部分只是生成一个介于 0 和 0x1000000 之间的随机数(== 16777216)。

但第二部分似乎很奇怪。执行位移 0 有什么意义?我不认为它会做任何事情。然而,经过进一步调查,我注意到移位 0 似乎截断了数字的小数部分。此外,它是右移还是左移,甚至是无符号右移都无关紧要。

> 10.12345 << 0
10
> 10.12345 >> 0
10
> 10.12345 >>> 0
10

我用 Firefox 和 Chrome 都测试过,行为是一样的。那么,这种观察的原因是什么?它只是 JavaScript 的细微差别,还是在其他语言中也存在?我以为我理解位移,但这让我很困惑。

4

5 回答 5

22

你是对的;它用于截断值。

之所以>>有效是因为它仅对 32 位整数进行操作,因此该值被截断。(它也常用于此类情况,而不是Math.floor因为按位运算符的运算符优先级较低,因此您可以避免括号混乱。)

而且由于它只对 32 位整数进行操作,因此它也相当于一个带有0xffffffff后舍入的掩码。所以:

0x110000000      // 4563402752
0x110000000 >> 0 // 268435456
0x010000000      // 268435456

但这不是预期行为的一部分,因为Math.random()它将返回一个介于 0 和 1 之间的值。

| 0此外,它与更常见的 做同样的事情。

于 2012-08-25T20:34:49.323 回答
12

Math.random()返回一个介于 0(包括)和 1(不包括)之间的数字。将此数字乘以整数会得到一个包含小数部分的数字。<<运算符是消除小数部分的快捷方式:

所有按位运算符的操作数都以大端顺序和二进制补码格式转换为有符号的 32 位整数。

上述语句意味着 JavaScript 引擎会将<<operator 的两个操作数隐式转换为 32 位整数;对于数字,它通过删除小数部分来实现(不适合 32 位整数范围的数字比小数部分更松散)。

它只是 JavaScript 的细微差别,还是在其他语言中也存在?

您会在松散类型的语言中注意到类似的行为。以 PHP 为例:

var_dump(1234.56789 << 0);
// int(1234)

对于强类型语言,程序通常会拒绝编译。C# 抱怨是这样的:

Console.Write(1234.56789 << 0);
// error CS0019: Operator '<<' cannot be applied to operands of type 'double' and 'int'

对于这些语言,您已经有了类型转换运算符:

Console.Write((int)1234.56789);
// 1234
于 2012-08-25T21:05:26.877 回答
6

来自按位运算符(包括移位运算符)的Mozilla 文档

所有按位运算符的操作数都以大端顺序和二进制补码格式转换为有符号的 32 位整数。

所以基本上代码使用移位运算符的那个有点偶然的方面作为它所做的唯一重要的事情,因为它移动了 0 位。伊克。

它只是 JavaScript 的细微差别,还是在其他语言中也存在?

当然,我不能代表所有语言,但 Java 和 C# 都不允许double值作为左操作数作为移位运算符。

于 2012-08-25T20:37:03.530 回答
3

根据 ECMAScript 语言规范:
http ://ecma-international.org/ecma-262/5.1/#sec-11.7.1

产生式 ShiftExpression : ShiftExpression >> AdditiveExpression 的评估如下:

  1. 令 lref 为评估 ShiftExpression 的结果。
  2. 令 lval 为 GetValue(lref)。
  3. 令 rref 为 AdditiveExpression 求值的结果。
  4. 设 rval 为 GetValue(rref)。
  5. 令 lnum 为 ToInt32(lval)。
  6. 设 rnum 为 ToUint32(rval)。
  7. 令 shiftCount 为屏蔽掉除 rnum 的最低有效 5 位以外的所有结果,即计算 rnum & 0x1F。
  8. 返回执行 lnum 符号扩展右移 shiftCount 位的结果。传播最高有效位。结果是一个有符号的 32 位整数。
于 2012-08-25T20:42:21.190 回答
3

您观察到的行为在ECMA-262 标准中定义

<<这是左移运算符规范的摘录:

产生式 ShiftExpression : ShiftExpression << AdditiveExpression 的评估如下:

  1. 令 lref 为评估 ShiftExpression 的结果。
  2. 令 lval 为 GetValue(lref)。
  3. 令 rref 为 AdditiveExpression 求值的结果。
  4. 设 rval 为 GetValue(rref)。
  5. 令 lnum 为 ToInt32(lval)。
  6. 设 rnum 为 ToUint32(rval)。
  7. 令 shiftCount 为屏蔽掉除 rnum 的最低有效 5 位以外的所有结果,即计算 rnum & 0x1F。
  8. 通过 shiftCount 位返回 lnum 左移的结果。结果是一个有符号的 32 位整数。

如您所见,两个操作数都转换为 32 位整数。因此小数部分消失了。

这同样适用于其他位移运算符。您可以在我链接到的文档的第11.7 节按位移位运算符中找到它们各自的描述。

在这种情况下,执行移位的唯一效果是类型转换。Math.random()返回一个浮点值。

于 2012-08-25T20:43:41.533 回答