我最近发现了这段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的细微差别,还是也出现在其他语言中?我以为我理解了位移,但这令我感到困惑。
答案 0 :(得分:18)
你是对的;它用于截断值。
>>
工作的原因是因为它仅在32位整数上运行,因此该值被截断。 (它也常用于像这样的情况而不是Math.floor
,因为按位运算符的运算符优先级较低,所以你可以避免使用括号。)
由于它仅在32位整数上运行,因此在舍入后它也相当于带有0xffffffff
的掩码。所以:
0x110000000 // 4563402752
0x110000000 >> 0 // 268435456
0x010000000 // 268435456
但这不是预期行为的一部分,因为Math.random()
将返回介于0和1之间的值。
此外,它与| 0
完全相同,这是更常见的。
答案 1 :(得分:9)
Math.random()
返回0(包括)和1(不包括)之间的数字。将此数字乘以整数会产生一个具有小数部分的数字。 <<
运算符是消除小数部分的快捷方式:
所有位运算符的操作数都转换为带符号的32位 big-endian顺序和二进制补码格式的整数。
上述语句意味着JavaScript引擎将隐式地将<<
运算符的两个操作数转换为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
答案 2 :(得分:6)
来自Mozilla documentation of bitwise operators(包括班次运营商)
所有位运算符的操作数都以big-endian顺序和2的补码格式转换为带符号的32位整数。
所以基本上代码使用移位运算符的某种偶然方面作为由于移位0位而唯一重要的事情。 ICK。
它只是JavaScript的细微差别,还是也出现在其他语言中?
我当然不能说所有语言,但Java和C#都不允许double
值作为左操作数的移位运算符。
答案 3 :(得分:3)
根据ECMAScript语言规范:
http://ecma-international.org/ecma-262/5.1/#sec-11.7.1
制作ShiftExpression:ShiftExpression&gt;&gt; AdditiveExpression 评估如下:
- 让lref成为评估ShiftExpression的结果。
- 让lval成为GetValue(lref)。
- 让rref成为评估AdditiveExpression的结果。
- 让rval为GetValue(rref)。
- 让lnum成为ToInt32(lval)。
- 让rnum成为ToUint32(rval)。
- 让shiftCount成为屏蔽rnum除了最低5位之外的所有位的结果,即计算rnum&amp;为0x1F。
- 返回通过shiftCount位执行lnum的符号扩展右移的结果。传播最重要的位。该 result是带符号的32位整数。
醇>
答案 4 :(得分:3)
您正在观察的行为在ECMA-262 standard
中定义以下是<<
左移运算符规范的摘录:
生产ShiftExpression:ShiftExpression&lt;&lt; AdditiveExpression 评估如下:
- 让lref成为评估ShiftExpression的结果。
- 让lval成为GetValue(lref)。
- 让rref成为评估AdditiveExpression的结果。
- 让rval为GetValue(rref)。
- 让lnum成为ToInt32(lval)。
- 让rnum成为ToUint32(rval)。
- 让shiftCount成为屏蔽rnum除了最低5位之外的所有位的结果,即计算rnum&amp;为0x1F。
- 通过shiftCount位返回左移lnum的结果。结果是带符号的32位整数。
醇>
如您所见,两个操作数都转换为32位整数。因此小数部分的消失。
这同样适用于其他位移运算符。您可以在我链接到的文档的 11.7按位移位运算符部分找到各自的说明。
在这种情况下,执行移位的唯一效果是类型转换。 Math.random()
返回浮点值。