为什么0乘以截断小数?

时间:2012-08-25 20:33:03

标签: javascript numbers operators bitwise-operators bit-shift

我最近发现了这段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的细微差别,还是也出现在其他语言中?我以为我理解了位移,但这令我感到困惑。

5 个答案:

答案 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   评估如下:

     
      
  1. 让lref成为评估ShiftExpression的结果。
  2.   
  3. 让lval成为GetValue(lref)。
  4.   
  5. 让rref成为评估AdditiveExpression的结果。
  6.   
  7. 让rval为GetValue(rref)。
  8.   
  9. 让lnum成为ToInt32(lval)。
  10.   
  11. 让rnum成为ToUint32(rval)。
  12.   
  13. 让shiftCount成为屏蔽rnum除了最低5位之外的所有位的结果,即计算rnum&amp;为0x1F。
  14.   
  15. 返回通过shiftCount位执行lnum的符号扩展右移的结果。传播最重要的位。该   result是带符号的32位整数。
  16.   

答案 4 :(得分:3)

您正在观察的行为在ECMA-262 standard

中定义

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

  

生产ShiftExpression:ShiftExpression&lt;&lt; AdditiveExpression   评估如下:

     
      
  1. 让lref成为评估ShiftExpression的结果。
  2.   
  3. 让lval成为GetValue(lref)。
  4.   
  5. 让rref成为评估AdditiveExpression的结果。
  6.   
  7. 让rval为GetValue(rref)。
  8.   
  9. 让lnum成为ToInt32(lval)。
  10.   
  11. 让rnum成为ToUint32(rval)。
  12.   
  13. 让shiftCount成为屏蔽rnum除了最低5位之外的所有位的结果,即计算rnum&amp;为0x1F。
  14.   
  15. 通过shiftCount位返回左移lnum的结果。结果是带符号的32位整数。
  16.   

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

这同样适用于其他位移运算符。您可以在我链接到的文档的 11.7按位移位运算符部分找到各自的说明。

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