28

I recently found this piece of JavaScript code:

Math.random() * 0x1000000 << 0

I understood that the first part was just generating a random number between 0 and 0x1000000 (== 16777216).

But the second part seemed odd. What's the point of performing a bit-shift by 0? I didn't think that it would do anything. Upon further investigation, however, I noticed that the shift by 0 seemed to truncate the decimal part of the number. Furthermore, it didn't matter if it was a right shift, or a left shift, or even an unsigned right shift.

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

I tested both with Firefox and Chrome, and the behavior is the same. So, what is the reason for this observation? And is it just a nuance of JavaScript, or does it occur in other languages as well? I thought I understood bit-shifting, but this has me puzzled.

Salman A
  • 262,204
  • 82
  • 430
  • 521
voithos
  • 68,482
  • 12
  • 101
  • 116
  • See also http://stackoverflow.com/questions/3081987/what-good-does-zero-fill-bit-shifting-by-0-do-a-0 – John Carter Aug 25 '12 at 20:38
  • 1
    In other languages, it mostly doesn't work. Python, C#, VB.NET, Java, Ruby all disallow it, among... just about everything else. – Ry- Aug 25 '12 at 20:47
  • 1
    It does have the same effect in Perl, though. – Ry- Aug 25 '12 at 20:49

5 Answers5

24

You're correct; it is used to truncate the value.

The reason >> works is because it operates only on 32-bit integers, so the value is truncated. (It's also commonly used in cases like these instead of Math.floor because bitwise operators have a low operator precedence, so you can avoid a mess of parentheses.)

And since it operates only on 32-bit integers, it's also equivalent to a mask with 0xffffffff after rounding. So:

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

But that's not part of the intended behaviour since Math.random() will return a value between 0 and 1.

Also, it does the same thing as | 0, which is more common.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 2
    Can you elaborate on why `>>` works? Does it coerce the type into an integer? – voithos Aug 25 '12 at 20:36
  • 1
    @voithos: Yes. A 32-bit integer. – Ry- Aug 25 '12 at 20:38
  • @viothos: Read the paragraph again. Bitwise shifts operate on 32-bit integers, so it has to be coerced into one for the shift to be done. – Ken White Aug 25 '12 at 20:39
  • 2
    It doesn't round, it truncates. What are `-1.6 << 0` and `Math.round(-1.6)`? – mu is too short Aug 25 '12 at 20:40
  • @muistooshort: Good point! Even `Math.floor()` does something slightly different. – voithos Aug 25 '12 at 20:46
  • @minitech What does 32-bit integer, 16-bit integer, x-bit integer mean? – David G Aug 25 '12 at 20:50
  • 2
    @voithos: `floor` moves toward negative infinity, `ceil` towards positive infinity, `round` rounds, "casting" (or the JavaScript equivalent thereof) truncates. – mu is too short Aug 25 '12 at 20:53
  • @David: Computer represent numbers (integers) in a different way than we humans usually do. Perhaps [this article](http://en.wikipedia.org/wiki/Integer_(computer_science)) will give more info. – voithos Aug 25 '12 at 20:54
14

Math.random() returns a number between 0 (inclusive) and 1 (exclusive). Multiplying this number with a whole number results in a number that has decimal portion. The << operator is a shortcut for eliminating the decimal portion:

The operands of all bitwise operators are converted to signed 32-bit integers in big-endian order and in two's complement format.

The above statements means that the JavaScript engine will implicitly convert both operands of << operator to 32-bit integers; for numbers it does so by chopping off the fractional portion (numbers that do not fit 32-bit integer range loose more than just the decimal portion).

And is it just a nuance of JavaScript, or does it occur in other languages as well?

You'll notice similar behavior in loosely typed languages. PHP for example:

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

For strongly types languages, the programs will usually refuse to compile. C# complains like this:

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

For these languages, you already have type-casting operators:

Console.Write((int)1234.56789);
// 1234
Salman A
  • 262,204
  • 82
  • 430
  • 521
6

From the Mozilla documentation of bitwise operators (which includes the shift operators)

The operands of all bitwise operators are converted to signed 32-bit integers in big-endian order and in two's complement format.

So basically the code is using that somewhat-incidental aspect of the shift operator as the only significant thing it does due to shifting by 0 bits. Ick.

And is it just a nuance of JavaScript, or does it occur in other languages as well?

I can't speak for all languages of course, but neither Java nor C# permit double values to be the left operand a shift operator.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for mentioning Java and C#; I just check Python, and it also throws a `TypeError` if you try to shift a float by an int. – voithos Aug 25 '12 at 20:49
3

According to ECMAScript Language Specification:
http://ecma-international.org/ecma-262/5.1/#sec-11.7.1

The production ShiftExpression : ShiftExpression >> AdditiveExpression is evaluated as follows:

  1. Let lref be the result of evaluating ShiftExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating AdditiveExpression.
  4. Let rval be GetValue(rref).
  5. Let lnum be ToInt32(lval).
  6. Let rnum be ToUint32(rval).
  7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
  8. Return the result of performing a sign-extending right shift of lnum by shiftCount bits. The most significant bit is propagated. The result is a signed 32-bit integer.
Peter
  • 5,138
  • 5
  • 29
  • 38
3

The behavior you're observing is defined in the ECMA-262 standard

Here's an excerpt from the specification of the << left shift operator:

The production ShiftExpression : ShiftExpression << AdditiveExpression is evaluated as follows:

  1. Let lref be the result of evaluating ShiftExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating AdditiveExpression.
  4. Let rval be GetValue(rref).
  5. Let lnum be ToInt32(lval).
  6. Let rnum be ToUint32(rval).
  7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
  8. Return the result of left shifting lnum by shiftCount bits. The result is a signed 32-bit integer.

As you can see, both operands are cast to 32 bit integers. Hence the disappearance of decimal parts.

The same applies for the other bit shift operators. You can find their respective descriptions in section 11.7 Bitwise Shift Operators of the document I linked to.

In this case, the only effect of performing the shift is type conversion. Math.random() returns a floating point value.

toniedzwiedz
  • 17,895
  • 9
  • 86
  • 131