This article is part of series on numbers in JavaScript. The previous post1 is about integers and you should understand the gist of it for this one to make sense.

The operators themselves

The bitwise operators in JS are &, |, ^, ~, << and <<<. They represent the standard bitwise operations present in various programming languages. The corresponding MDN page is full of helpful examples on the topic. The only thing I want to remark on the operators themselves is that in JS we have two distinct operators for sign-propagating right shift (<<) or zero-fill right shift (<<<). So the bitwise operators work on integers the way they do in other languages as well. However, there are no integers in JavaScript as we have seen in the previous post1. The MDN page is not super clear on how operands are converted to integers. How is 0.1 & 0.5 computed for example? We need to consider the language specification for that.

ToInt32

Bitwise operators are defined in section 12.12 of the EcmaScript 2017 language specification. How operands have to be evaluated is defined by the semantics section 12.12.32. It looks like both operands are converted to a 32 bit integer using the operation toInt32. By going to its specification, we find the following definition:

  1. Let number be ? ToNumber(argument).
  2. If number is , , , , or , return .
  3. Let int be the mathematical value that is the same sign as number and whose magnitude is floor(abs(number)).
  4. Let int32bit be modulo .
  5. If int32bit , return int32bit ; otherwise return int32bit.

So we see that bitwise operators do not let us access bits of the underlying representation at all. Instead operands are mapped to 32 bit integers whose bits are (conceptually) used for the operation. Let’s verify that the algorithm above does not change values from the 32 bit two’s complement range, so we get the expected behavior of bitwise operators.

The 32 bit integers run from to . Let be an arbitrary one of those. We already have an integer, so steps 1 and 3 are doing nothing to it. Step 2 will return if , which satisfies our requirements. So we consider only and steps 4 and 5.

If step 4 and 5 will do nothing, because the highest possible value for is still smaller than and . However, a negative will be mapped to a positive one by the modulo operation in step 4. But because negative values are mapped to large positive values, step 5 reverts that. More precisely:

Therefore step 5 will return . So indeed, bitwise operations on 32 bit integer values will yield the same results as in languages that have 32 bit integers. We can learn two things from this, one useful and one rather useless:

  • Since the operation ToNumber maps anything (that I’ve tried so far) to a number, we can use bitwise operators not only on numbers but also strings or objects. This is the rather useless one, yup.

  • Although JavaScript defines only one floating point number type these operators make it look like integers are represented using 32 bit two’s complement. But this is only a temporary representation, the result will always be a regular floating point number.